2

New to Python and was curious if someone could explain, in layman's terms as much as possible, why it is that, when you're creating a subclass, you still need to call the parent initialization function?

Let's say you have a parent class like this:

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)

Then I want to make a subclass called "Dog" that obviously uses some of the functions in the parent class. My research is telling me that I need to do something like this:

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

But why, if I'm passing the parent class as an argument when defining the subclass, do I need to call the parent's __init__ ? Isn't this automatically passed to the subclass by virtue of the fact that it's a subclass?

I'm sure I'm missing something obvious here in my understanding, but it's just not clicking for me.

AdjunctProfessorFalcon
  • 1,790
  • 6
  • 26
  • 62
  • Not sure what you're asking. By making it a subclass, you make it inherit. Why would that automatically mean you want to call the parent's `__init__`? Also, how would it know whether you want to call the parent `__init__` before, after, or in the middle of the subclass `__init__`? – BrenBarn Aug 13 '15 at 18:46
  • 1
    There is actually a `super` keyword in Python, by the way. http://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods It's also changed a bit between V2 and V3. – bbill Aug 13 '15 at 18:46
  • @BrenBarn What I'm asking is: why does it work that way? If you're inheriting from the parent class, doesn't it stand to reason that you're then inheriting everything from the parent class, including the `__init__` ? – AdjunctProfessorFalcon Aug 13 '15 at 18:49
  • 3
    Yes, and you *do* inherit everything, but then you **override `__init__` in `Dog`**. If you didn't, the `Pet` version *would* get called when you did `dog = Dog(...)`. – jonrsharpe Aug 13 '15 at 18:50
  • @Malvin9000 Did you take a look at my link? `super` is the way of "implicitly" inheriting things like you're asking about. Nothing's stopping you from calling the parent explicitly. – bbill Aug 13 '15 at 18:50
  • It is just the way it is designed. It is because you have decided to override the method to add *stuff*. If you also want to maintain all of the ```super```'s functionality for that method, you have to call it. – wwii Aug 13 '15 at 18:51
  • 1
    @bbill Yes, thank you! – AdjunctProfessorFalcon Aug 13 '15 at 18:52

4 Answers4

6

What I'm asking is: why does it work that way? If you're inheriting from the parent class, doesn't it stand to reason that you're then inheriting everything from the parent class, including the __init__ ?

Not if you're overriding it. If your subclass doesn't define its own __init__, then the superclass __init__ is indeed called. But if the subclass does define its own __init__, then you're overriding it, so Python doesn't make asusumptions about how or whether you want to call the superclass implementation.

This gives you additional flexibility. If the superclass __init__ were called automatically, it would have to be uniformly called before or after the subclass __init__. But since it's not called automatically, you can call it whenever you like. You can call it at the beginning of the subclass __init__, at the end, somewhere in the middle, or not at all. This means that your subclass can either "set up" things before the superclass method acts, or "tweak" things after the superclass method acts, or do both, or completely replace the superclass functionality.

Also, if the superclass implementation were called automatically, it would be unclear what to do if the subclass didn't accept the same arguments as the superclass. With explicit superclass calling, your subclass can accept more or fewer arguments than its superclass, and can decide itself what to pass when calling the superclass.

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
2

Because the parent class might do something interesting in it's constructor. When you define a subclass of Pet with its own constructor, you're overriding that behavior with your new constructor, so saving the name and species of the pet never happens. To fix this, you can call the parent class's constructor as the first thing you do in your new constructor, restoring the important behavior of saving name and species.

You might be wondering, why not have every subclass save the name and species of the pet individually? (Apologies if you aren't - hopefully this answer will help someone who is.) There's a principle in programming called DRY - don't repeat yourself. Since the behavior of saving name and species is common to all pets, it makes sense to implement it in the Pet class. If you say that subclasses have to do it themselves, then 1. you are repeating yourself, and 2. you have to trust yourself to remember to do it when you make a new Pet subclass later.

zrneely
  • 1,802
  • 3
  • 17
  • 26
1

Python does gives you more flexibility to deal with base class entities, in the face of overriding you can still call the base methods/attributes by using super or even base class.

It's not that you are insisting to call the base.__init__, you don't need write __init__ in the both classes unless you wanted to define different properties for your scope. here you've added additional attributes to your scope and like chases_cats and you are trying to get the attributes of base to be included into your scope.

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

You can even define your child scope with out initiating base attributes, it nothing but you are not interested in parent attributes and overriding it.

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

And also it's not that you've to initiate your base attributes in the subclass initializer, any where in the inherited scope you can initialize base attributes when it's required.

gsb-eng
  • 1,211
  • 1
  • 9
  • 16
0

I am no expert here, but I do subclass many of the tkinter widget classes on a regular basis like you are doing, and I offer my two cents

You typically want to initialize any Pet class attributes according to your preference, which is why you would do Pet.__init__. Further, you might improve your code by passing the args directly to Pet:

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

dog = Dog(chases_cats=True,name='max',species='Dog') 

if dog.chases_cats:
    print ("My %s, %s, enjoys chasing cats"%(dog.getSpecies(), dog.getName()))
#My Dog, max, enjoys chasing cats
dermen
  • 5,252
  • 4
  • 23
  • 34