[Tutor] difference between super() and parent.__init__()?

Steven D'Aprano steve at pearwood.info
Sat Oct 22 02:29:56 CEST 2011


Alex Hall wrote:
> Hi all,
> I am just curious: I have seen classes that are subclasses initialize
> their parents through both super and parentClass.__init__. What is the
> difference, if any, and is one "better" or "more pythonic" than the
> other?


A simple question with a complicated answer...


First off, the answer depends on whether you are using so-called 
"classic classes" or "new-style classes".

If you are using Python 3, there are only new-style classes: classic 
classes are gone. But in Python 2, classic classes are written like this:

class MyClass:  # Don't inherit from anything.
     pass


while new-style classes inherit from either object or some other 
built-in type which inherits from object:

class MyClass(object):  # Inherit from object, new-style class.
     pass
class MyInt(int): pass
class MyStr(str): pass
class MyList(list): pass
# etc.


super *does not work* for classic classes, end of story. So in the 
following discussion, I'm only talking about new-style classes, the only 
sort of class in Python 3.

Secondly, let's talk about the difference between simple, single 
inheritance and multiple inheritance. Single inheritance is when each 
class inherits from exactly one parent, or superclass:

class A(object): pass
class B(A): pass
class C(B): pass

gives an inheritance hierarchy (a family tree, if you like):

object
   |
   A
   |
   B
   |
   C


In the case of single inheritance, super doesn't do anything special. It 
is the recommended way to handle inheritance: you are encouraged to 
write something like this:

class C(B):
     def method(self, arg):
         x = super().method(arg)  # Only works in Python 3
         # or in Python 2, use super(C, self).method(arg)

instead of x = B.method(arg), but functionality-wise, there is no 
difference between the two. In Python 3, super becomes a little more 
convenient, but it doesn't really matter much which you use. (But keep 
reading, for a reason why you *should* use super even in single 
inheritance.)

The situation is more complicated once you have multiple inheritance. In 
multiple inheritance, at least one of the classes involved inherits from 
two or more superclasses. Here's a particularly simple example:

class A(object): pass
class B(A): pass
class C(A): pass
class D(B, C): pass  # multiple superclasses

or:

object
   |
   A
  / \
B   C
  \ /
   D


In full generality, multiple inheritance can be so complex that most 
programming languages prohibit it, or put severe restrictions on it. 
Python is one of the very few that support it with very few restrictions.

The difficulty occurs when you override a method in D. Now you need to 
ensure that each superclass (B and C) gets a shot at having their method 
called too. Here is a simple example showing how NOT to do it:

class A(object):
     def save(self):
         print("class A saves")

class B(A):
     def save(self):
         print("B saves stuff")
         A.save(self)  # call the parent class method too

class C(A):
     def save(self):
         print("C saves stuff")
         A.save(self)

class D(B, C):
     def save(self):
         print ("D saves stuff")
         # make sure you let both B and C save too
         B.save(self)
         C.save(self)


Simple, obvious... but wrong. Try it and see:

 >>> d = D()
 >>> d.save()
D saves stuff
B saves stuff
class A saves
C saves stuff
class A saves

The problem is that the A.save method gets called twice.

In general, it is very hard to solve this problem by hand, especially if 
you have a complex family tree. You have to manage the entire family 
tree, keeping track of which superclasses you have already called, and 
avoid calling them a second time. That's fiddly and annoying and hard to 
do right.

But super can do it for you. Here's a version that works correctly:

class A(object):
     def save(self):
         print("class A saves")

class B(A):
     def save(self):
         print("B saves stuff")
         super(B, self).save()
         # In Python 3, you can abbreviate this as super().save()

class C(A):
     def save(self):
         print("C saves stuff")
         super(C, self).save()

class D(B, C):
     def save(self):
         print ("D saves stuff")
         super(D, self).save()


and in use:

 >>> d = D()
 >>> d.save()
D saves stuff
B saves stuff
C saves stuff
class A saves

Now each class gets called exactly once, and in the right order.

All the smarts managing the entire inheritance hierarchy is built into 
super, so each method gets called once and exactly once, provided that 
every class *only* uses super. If you try to mix super calls and direct 
method calls like B.method(self, arg), it probably won't work correctly, 
and if it does, it will probably be by accident.

So please use super, even in single inheritance. Otherwise you are 
restricting the usefulness of your class: it can never be used with 
multiple inheritance.

The one exception to this is if your class changes the method signature. 
E.g. if A.method takes no arguments, but B.method requires an argument. 
super cannot help you now. But changing the signature of methods is 
almost always the wrong thing to do: it is a violation of good object 
oriented design, and should be avoided. The main time it is justified is 
in the class constructor methods, __new__ or __init__, in which case you 
may need to avoid super and *carefully* manage the inheritance by hand.



-- 
Steven



More information about the Tutor mailing list