Problem
I’m quite new to Python object-oriented programming, and I’m having problems grasping the super() method (new style classes), particularly when it comes to multiple inheritance.
If you have something like this, for example:
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’m not sure about is whether or not the Third() class will inherit both constructor methods. If so, which one will be executed using super() and why?
What if you wish to take over the other? I’m guessing it has something to do with the order in which Python methods are resolved (MRO).
Asked by Callisto
Solution #1
Guido explains this in his blog post Method Resolution Order in a good bit of detail (including two earlier attempts).
Third(), in your case, will call First. __init__. Python searches the parents of the class from left to right for each attribute. We’re looking for __init__ in this situation. As a result, if you declare
class Third(First, Second):
...
Python will look at First first, and if First does not have the attribute, it will move on to Second.
When inheritance crosses tracks, the problem becomes more complicated (for example if First inherited from Second). For further information, see the link above, but in a nutshell, Python will try to keep the order in which each class appears on the inheritance list, starting with the child class.
As an example, if you had:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First):
def __init__(self):
print "third"
class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print "that's it"
[Fourth, Second, Third, First] would be the MRO.
By the way, if Python can’t discover a logical method resolution order, it’ll throw an exception rather than fall back to unexpected behavior.
An unclear MRO is as follows:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
print "third"
Should Third’s MRO be [First, Second] or [Second, First]? Python will raise an error if there is no evident expectation:
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First
model. However, my purpose here is to keep things simple and demonstrate how the MRO is constructed. And it’s constructed like follows:
>>> Fourth.__mro__
(<class '__main__.Fourth'>,
<class '__main__.Second'>, <class '__main__.Third'>,
<class '__main__.First'>,
<type 'object'>)
Answered by rbp
Solution #2
Your code, as well as the other responses, are all flawed. They are missing the super() calls that are essential for cooperative subclassing to work in the first two classes.
The following is a corrected version of the code:
class First(object):
def __init__(self):
super(First, self).__init__()
print("first")
class Second(object):
def __init__(self):
super(Second, self).__init__()
print("second")
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("third")
At each step, the super() call locates the next method in the MRO, which is why it’s required in First and Second; otherwise, execution would halt at the end of Second. __init__().
What I get is this:
>>> Third()
second
first
third
Answered by lifeless
Solution #3
I wanted to expand on the response a little because I wasn’t sure how to utilize super() in a multiple inheritance hierarchy in Python when I first started reading about it.
What you need to know is that super(MyClass, self). init () gives the next init method in the context of the entire inheritance tree, according to the employed Method Resolution Ordering (MRO) methodology.
This final section is critical to comprehend. Consider the following scenario once more:
#!/usr/bin/env python2
class First(object):
def __init__(self):
print "First(): entering"
super(First, self).__init__()
print "First(): exiting"
class Second(object):
def __init__(self):
print "Second(): entering"
super(Second, self).__init__()
print "Second(): exiting"
class Third(First, Second):
def __init__(self):
print "Third(): entering"
super(Third, self).__init__()
print "Third(): exiting"
The order to resolve __init__ is calculated (before Python 2.3) using a “depth-first left-to-right traversal,” according to Guido van Rossum’s essay on Method Resolution Order:
Third --> First --> object --> Second --> object
We get the following after deleting all duplicates save the last one:
Third --> First --> Second --> object
So, let’s see what happens when we create a Third class instance, e.g. x = Third ().
This explains why using Third() produces the following:
Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting
From Python 2.3 onwards, the MRO technique has been modified to perform well in complex cases, but I believe that using “depth-first left-to-right traversal” + “removing duplicates except for the last” still works in most cases (please comment if this is not the case). Make sure to read Guido’s blog article!
Answered by Visionscaper
Solution #4
The Diamond Problem is described on the page, and in short, Python will call the superclass’s methods from left to right.
Answered by monoceres
Solution #5
Although this isn’t a direct response to the super() question, I believe it is important enough to discuss.
There’s also a way to call each inherited class directly:
class First(object):
def __init__(self):
print '1'
class Second(object):
def __init__(self):
print '2'
class Third(First, Second):
def __init__(self):
Second.__init__(self)
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.
Answered by Seaux
Post is based on https://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance