4

Possible Duplicate:
Why aren't Python's superclass init methods automatically invoked?

For example:

class Pet(object):

    def __init__(self, name, species):
       self.name = name
       self.species = species

    def getName(self):
     return self.name

    def getSpecies(self):
     return self.species

    def __str__(self):
     return "%s is a %s" % (self.name, self.species)

class Dog(Pet):

   def __init__(self, name, chases_cats):
      Pet.__init__(self, name, "Dog")
      self.chases_cats = chases_cats

   def chasesCats(self):
     return self.chases_cats

As you can see Dog inherits from pet. I understand the code perfectly fine. But why must we call the init for pet in the Dog class? Why isn't just calling it as in the first line of the dog class enough ( class Dog(Pet) )? It seems to only create messier code. It kind of kills the point of inheritance in Python to me.

Community
  • 1
  • 1
VPNTIME
  • 721
  • 2
  • 8
  • 13
  • 4
    If you didn't call `Pet.__init__()`, the `Pet.species` attribute would never be initialised. You might think that `Pet.__init__()` should be called automatically, but there's no intelligent way to do this when the `__init__()` function has to be called with arguments. – Li-aung Yip May 09 '12 at 03:08
  • I updated my answer to provide a bit more information on using positional arguments (e.g. *args) and keyword arguments (e.g. **kwargs) – Casey Kuball May 09 '12 at 03:25
  • 1
    @Li-aungYip Agreed, in C++, when constructors have argument, they must be called explicitly in child as well. – zinking May 09 '12 at 03:29
  • @Omerta I would recommend removing those `getName`, and `getSpecies` functions. If you need to later on change how they are accessed, use a python [property](http://docs.python.org/library/functions.html#property), which will not change the syntax for anything using the class. See [this great article](http://tomayko.com/writings/getters-setters-fuxors) on why this is a great language feature, and why not to write getters/setters. – Casey Kuball May 09 '12 at 04:02

2 Answers2

9

Having the language force the super class to initialize before or after means that you lose functionality. A subclass may depend on a superclass's initialization to be run first, or vice versa.

In addition, it wouldn't have any way of knowing what arguments to pass -- subclasses decide what values are passed to an initializer, which gives more flexibility over automatically passing all arguments.

An alternative method of initializing the super-class is by using super, though this will actually initialize the super-class of self dynamically, by looking it up in the object's __mro__ (method resolution order)

In python 2:

super(self, Dog).__init__(self, name, "Dog")

In python 3, you can further reduce the syntax, and avoid repeating yourself:

super().__init__(self, name, "Dog")

Edit:

Since you're not actually using the name argument passed to dog, you can further reduce some syntax, by accepting arbitrary keyword and positional arguments in the Dog initializer, and passing them up the initialization chain:

class Dog(Pet):

   def __init__(self, chases_cats, *args, **kwargs):
      Pet.__init__(self, *args, species="Dog", **kwargs)
      self.chases_cats = chases_cats

class Pet(object):

    def __init__(self, name, species, *args, **kwargs):
       self.name = name
       self.species = species

In your scenario, the initializer might be simple enough to not need this, but it's useful to know for when you have more arguments, and/or a deeper class hierarchy.

Casey Kuball
  • 7,717
  • 5
  • 38
  • 70
4

You can make species a class attribute like this

class Pet(object):
    species = None

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

    def getName(self):
        return self.name

    def getSpecies(self):
        return self.species

    def __str__(self):
        return "%s is a %s" % (self.name, self.species)

class Dog(Pet):
    species = "Dog"

    def __init__(self, name, chases_cats):
        Pet.__init__(self, name)
        self.chases_cats = chases_cats

    def chasesCats(self):
        return self.chases_cats
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • Yes, actually I like this design more, why have to initialize the species in base class, that's odd for OO perspective. – zinking May 09 '12 at 03:34
  • @zinking, you don't _have_ to. Some people prefer it others don't – John La Rooy May 09 '12 at 03:37
  • This design does mean that you can't change the species for a specific instance. If the species is supposed to be equal to the class name, you can just define it as a [property](http://docs.python.org/library/functions.html#property) on `Pet`: `def species(self): return type(self).__name__`. This provides all the functionality of this, except for a `Pet` object having a species of `None`. – Casey Kuball May 09 '12 at 04:00
  • @Darthfett, What do you mean? I can easily set an instance attribute called `species` to override the class attribute. – John La Rooy May 09 '12 at 04:08
  • @gnibbler my mistake, I thought it would end up changing the class attribute. I also learned class attributes can be accessed from the instance (I thought it was strictly from the class name), so I guess I should have tested that first. Thanks for the correction. – Casey Kuball May 09 '12 at 04:52