16
class Animal(object):
    def __init__(self, nlegs=4):
        self.nlegs = nlegs
    
class Cat(Animal):
    def __init__(self, talk='meow'):
        self.talk = talk

class Dog(Animal):
    def __init__(self, talk='woof'):
        self.talk = talk
  1. Why does my cat tom = Cat() not have an nlegs attribute?
  2. Should we explicitly call Animal.__init__() from within Cat.__init__, or should we use super here?
  3. What if we want to create a cat with 5 legs, should we add additional arguments to the Cat.__init__ interface?

3 Answers 3

17

To build on what everyone else has said, yes, you'll need to call the parent's __init__ method.

It's generally best to use super. However, in certain cases (particularly when you're inheriting from multiple classes) it can be a big gotcha. I'll avoid going into detail, there are no shortage of various articles which discuss it. (Also, there are some oddities with some of the other "special" functions. For example, you can do super(SomeCls, self).__getitem__(5) but super(SomeCls, self)[5] won't work.)

As a simplistic example of why it's a good idea to use it, you could make Dog and Cat inherit from Mammal (which inherits from Animal) and not have to change places in your code other than which class Dog and Cat inherit from.

As for why your tom instance doesn't have tom.nlegs, it's because you haven't called Animal's __init__ method.

Also remember that not everything needs to be set at initialization time. For this example, it makes more sense not to set things like nlegs in the __init__ method. Instead, just set it directly in the class. E.g.

class Mammal(object):
    nlimbs = 4
    def __init__(self):
        print "I'm a mammal!"

class Cat(Mammal):
    def __init__(self, color="calico"):
        self.color = color
        super(Cat, self).__init__()
        print "I have {0} legs I am {1}".format(self.nlimbs, self.color)

class FiveLeggedCat(Cat):
    nlimbs = 5

Basically, if something is likely to change from instance to instance (e.g. the color of the cat) or needs to be done at initialization (e.g. opening a file), then it probably should be set in __init__.

Otherwise, if it's something we want to be the same for any instance of the class, it can be cleaner to set it directly in the class definition.

Also, attributes set this way will be available to documentation tools (e.g. the built-in help function), whereas attributes set at initialization won't be.

Sign up to request clarification or add additional context in comments.

3 Comments

@wim - I added some edits to clarify things a bit. In a nutshell, if it's something that's going to change with every instance, then set in in the __init__ constructor. Otherwise, make the __init__ function simpler, and set it directly in the class definition. Hope that helps!
I don't think every animal has 4 legs (some don't have any). So, it shouldn't be part of Animal class.
@JBernardo - True, and good point! I made it into a mammal class instead. Excepting special cases such as whales (which still have legs, they've just become vestigial internal bones) most (un injured/mutant) mammals have 4 limbs, so it's a better fit.
4

Use super:

class Cat(Animal):
    def __init__(self, talk='meow', num_legs=4):
        print 'Hay cat'
        self.talk = talk
        super(Cat, self).__init__(num_legs)


tom = Cat()  #tom is a normal cat
bob = Cat('Nyan', 3) #bob is a japanese defective cat

1 Comment

Yes. The derived class use the parent __init__ method if it doesn't define one. You don't need to do anything to object.
2

You need to look at the Python docs about super(). For example, you would normally begin (or end) your Cat.__init__() method with a call to super(Cat, self).__init__(<any args>).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.