zonksoft
zonksoft

Reputation: 2429

Type error with classmethod constructors

I'm implementing several constructors using @classobj. I'm not only setting variables, but also calling methods in the new class:

class Example:
    def __init__(self):
        pass

    @classmethod
    def constructor1(cls,x,y):
        self=cls
        self.__x = x
        self.__somemethod(self,y)

    ...

I get the following error:

unbound method __somemethod() must be called with Example instance as 
first argument (got classobj instance instead)

How do I resolve this problem?

Upvotes: 1

Views: 731

Answers (4)

ch3ka
ch3ka

Reputation: 12158

looks like __somemethod is not a classmethod, but a "normal" method. And normal methods expect an actual instance as the first parameter, not a class. And because constructor1 is decorated as a @classmethod, cls is the class itself - which you pass to __somemethod. That cannot work.

You should reconsider your design approach.

Addendum:

Maybe you meant something like this?

@classmethod
def constructor1(cls, x, y):
    newinst = cls()
    newinst.__x = x
    cls.__somemethod(newinst, y)

That'd be better written as followed, though:

@classmethod
def constructor1(cls, x, y):
    newinst = cls()
    newinst.__x = x
    newinst.__somemethod(y)

actually, I like neighter approach - seems like a codesmell of overcomplexity to me.

Upvotes: 2

Blckknght
Blckknght

Reputation: 104712

If you're wanting your class method to be a constructor, you probably want to be creating an instance of the class you get passed in as cls. I suspect you're trying to do that with your self = cls line, but you're not actually creating a new instance because you've neglected to put parentheses. There are some other issues too, but I think that is the key one. Here's a fixed constructor:

@classmethod
def constructor1(cls,x,y):
    self=cls()                # parentheses added, to make it a call
    self.__x = x
    self.__somemethod(y)      # self is not needed as a parameter here
    return self               # return the new instance

Upvotes: 3

zonksoft
zonksoft

Reputation: 2429

Thanks to ch3ka's answer and Tim Pietzcker's comment, I found my error: I used the factory method from http://jjinux.blogspot.co.at/2008/11/python-class-methods-make-good.html and forgot the () in the line self=cls(). Now it works just fine:

class Example:
    def __init__(self):
        pass

    @classmethod
    def constructor1(cls,x,y):
        self=cls()
        self.__x = x
        self.__somemethod(self,y)

    ...

Upvotes: 1

Jon Clements
Jon Clements

Reputation: 142156

This may be a template of what I think you're trying to achieve...

import random

class Something(object):
    def __init__(self, value, **kwargs):
        self.value = value
        for k, v in kwargs.iteritems():
            setattr(self, k, v)
    @classmethod
    def from_iterable(cls, iterable):
        return cls(sum(iterable), description='came from from_iterable')
    @classmethod
    def make_random(cls):
        return cls(random.randint(1,1000), is_random=True)

a = Something.from_iterable([1, 2, 3])
b = Something.make_random()
c = Something(56)

for obj in (a, b, c):
    print type(obj), obj.value

<class '__main__.Something'> 6
<class '__main__.Something'> 308
<class '__main__.Something'> 56

Upvotes: 1

Related Questions