[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