I'm pretty much new in Python object oriented programming and I have trouble
understanding the super()
function (new style classes) especially when it comes to multiple inheritance.
For example if you have something like:
class First(object):
def __init__(self):
print "first"
class Second(object):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
What I don't get is: will the Third()
class inherit both constructor methods? If yes, then which one will be run with super() and why?
And what if you want to run the other one? I know it has something to do with Python method resolution order (MRO).
This is detailed with a reasonable amount of detail by Guido himself in his blog post Method Resolution Order (including two earlier attempts).
In your example,
Third()
will callFirst.__init__
. Python looks for each attribute in the class's parents as they are listed left to right. In this case we are looking for__init__
. So, if you definePython will start by looking at
First
, and, ifFirst
doesn't have the attribute, then it will look atSecond
.This situation becomes more complex when inheritance starts crossing paths (for example if
First
inherited fromSecond
). Read the link above for more details, but, in a nutshell, Python will try to maintain the order in which each class appears on the inheritance list, starting with the child class itself.So, for instance, if you had:
the MRO would be
[Fourth, Second, Third, First].
By the way: if Python cannot find a coherent method resolution order, it'll raise an exception, instead of falling back to a behaviour which might surprise the user.
Edited to add example of an ambiguous MRO:
Should
Third
's MRO be[First, Second]
or[Second, First]
? There's no obvious expectation, and Python will raise an error:Edit: I see several people arguing that the examples above lack
super()
calls, so let me explain: The point of the examples is to show how the MRO is constructed. They are not intended to print "first\nsecond\third" or whatever. You can – and should, of course, play around with the example, addsuper()
calls, see what happens, and gain a deeper understanding of Python's inheritance model. But my goal here is to keep it simple and show how the MRO is built. And it is built as I explained:I wanted to elaborate the answer by lifeless a bit because when I started reading about how to use super() in a multiple inheritance hierarchy in Python, I did't get it immediately.
What you need to understand is that
super(MyClass, self).__init__()
provides the next__init__
method according to the used Method Resolution Ordering (MRO) algorithm in the context of the complete inheritance hierarchy.This last part is crucial to understand. Let's consider the example again:
According to this article about Method Resolution Order by Guido van Rossum, the order to resolve
__init__
is calculated (before Python 2.3) using a "depth-first left-to-right traversal" :After removing all duplicates, except for the last one, we get :
So, lets follow what happens when we instantiate an instance of the
Third
class, e.g.x = Third()
.According to MRO
__init__
of Third is called first.Next, according to the MRO, inside the
__init__
methodsuper(Third, self).__init__()
resolves to the__init__
method of First, which gets called.Inside
__init__
of Firstsuper(First, self).__init__()
calls the__init__
of Second, because that is what the MRO dictates!Inside
__init__
of Secondsuper(Second, self).__init__()
calls the__init__
of object, which amounts to nothing. After that "second" is printed.After
super(First, self).__init__()
completed, "first" is printed.After
super(Third, self).__init__()
completed, "that's it" is printed.This details out why instantiating Third() results in to :
The MRO algorithm has been improved from Python 2.3 onwards to work well in complex cases, but I guess that using the "depth-first left-to-right traversal" + "removing duplicates expect for the last" still works in most cases (please comment if this is not the case). Be sure to read the blog post by Guido!
I understand this doesn't directly answer the
super()
question, but I feel it's relevant enough to share.There is also a way to directly call each inherited class:
Just note that if you do it this way, you'll have to call each manually as I'm pretty sure
First
's__init__()
won't be called.This is to how I solved to issue of having multiple inheritance with different variables for initialization and having multiple MixIns with the same function call. I had to explicitly add variables to passed **kwargs and add a MixIn interface to be an endpoint for super calls.
Here
A
is an extendable base class andB
andC
are MixIn classes both who provide functionf
.A
andB
both expect parameterv
in their__init__
andC
expectsw
. The functionf
takes one parametery
.Q
inherits from all three classes.MixInF
is the mixin interface forB
andC
.Your code, and the other answers, are all buggy. They are missing the
super()
calls in the first two classes that are required for co-operative subclassing to work.Here is a fixed version of the code:
The
super()
call finds the next method in the MRO at each step, which is why First and Second have to have it too, otherwise execution stops at the end ofSecond.__init__()
.This is what I get:
Overall
Assuming everything descends from
object
(you are on your own if it doesn't), Python computes a method resolution order (MRO) based on your class inheritance tree. The MRO satisfies 3 properties:If no such ordering exists, Python errors. The inner workings of this is a C3 Linerization of the classes ancestry. Read all about it here: https://www.python.org/download/releases/2.3/mro/
Thus, in both of the examples below, it is:
When a method is called, the first occurrence of that method in the MRO is the one that is called. Any class that doesn't implement that method is skipped. Any call to
super
within that method will call the next occurrence of that method in the MRO. Consequently, it matters both what order you place classes in inheritance, and where you put the calls tosuper
in the methods.With
super
first in each methodChild()
Outputs:With
super
last in each methodChild()
Outputs: