3

I am trying to make a subclass from a class with a @classmethod defined:

class Pet(object):
    def __init__(self,animal):
        self.animal = animal

    @classmethod
    def wild(cls,animal):
        return cls('wild '+animal)

class Cat(Pet):
    def __init__(self):
        Pet.wild('cat')

if __name__ == '__main__':
    print Cat().animal

This gives me the following error:

print Cat().animal
AttributeError: Cat instance has no attribute 'animal'

Probably I don't fully understand the concept of subclassing, I have been also trying to pass self to Pet.wild without much more success. Any help would be really appreciated!

EDIT

I try to explain what I want to do: I would like to initialize parentclass (or superclass?) Pet from the Cat subclass using the classmethod wild and not using __init__. Is this possible? Thank you

mcave
  • 121
  • 2
  • 7

2 Answers2

4

That is because you are not calling super to properly inherit from your base class. So, doing this:

Pet.wild('cat')

does not do what you expect.


class Pet(object):
    def __init__(self,animal):
        self.animal = animal

    @classmethod
    def wild(cls,animal):
        return cls('wild '+animal)

class Cat(Pet):
    def __init__(self):
        super(Cat, self).__init__('cat')

if __name__ == '__main__':
    print Cat().animal
idjaw
  • 25,487
  • 7
  • 64
  • 83
  • furthermore, "Pet.wild('cat')" does nothing (this is a function, returning new object, and discarding it) – lejlot Nov 18 '15 at 16:30
  • 2
    The `animal = 'pig'` in your answer does nothing. – Daenyth Nov 18 '15 at 16:31
  • It was a copy paste from OP. I can remove it. – idjaw Nov 18 '15 at 16:32
  • 1
    Yes, this is the "standard" subclassing. But if I want to get 'wild cat' as initialization of animal this does not work. My idea was to try to initialize the Cat class without calling directly Pet.__init__() but just by using the other classmethod – mcave Nov 18 '15 at 16:38
2

So if you want to add inheritance and extend Cat later here is how you would do it. See this post for the difference between __new__ and __init__

class Pet(object):

    def __init__(self, animal):
        self.animal = animal

    @classmethod
    def wild(cls, animal):
        obj = cls.__new__(cls)
        obj.animal = "wind " + animal
        return obj 

class Cat(Pet):
    pass


if __name__ == "__main__":
    print Pet.wild("cat").animal
    cat = Cat.wild("cat")
    house_cat = Cat("cat")
    print house_cat.animal, cat.animal

Here is what is going on under the covers. Cat inherits the wild classmethod too. So you don't have to call Pet.wild from within Cat constructor. You just call the wild classmethod directly on Cat. cls gets the Cat class and so the object created will be Cat instance and not a Pet instance. However, at some point you will have to override some default behavior in constructor and that involves some amount of coding.

Community
  • 1
  • 1
shreyas
  • 76
  • 2
  • thanks, but say that more in general I don't want to re-pass `"cat"` all the time and add some specific attributes to the `Cat` which `Pet` does not have. By my understanding that's the idea of making subclasses, this is just a simplified example. – mcave Nov 18 '15 at 16:56
  • 2
    If you write an `__init__` method in your subclass, it will override the `Pet` __init__ method. The only way to get `animal` attribute is to call the Parent class' __init__ method. Also __init__ is different in a way that it doesn't return anything. So your return statement doesn't do anything within init – shreyas Nov 18 '15 at 17:00
  • thanks a lot! I didn't realize I was overwriting `__init__`. But then why don't I get an error when I call `Pet.wild('cat')`? If I understood correctly, this should than call the `Cat.__init__` (if I have overwritten `Pet.__init__`) which does not allow any input – mcave Nov 18 '15 at 17:08
  • The code snippet above does not call `__init__` explicitly. If you want that you should change the `__new__` to `__init__`. Which would call the default constructor and then you can just override the attributes you want to be different in a `wild` Pet – shreyas Nov 18 '15 at 17:10
  • Also see my edit to the answer which shows how to use the classmethod from subclasses. – shreyas Nov 18 '15 at 17:12