A question about diamond shape inheritance in python:
A
/ \
B C
\ /
D
I first created this
class A:
def __init__(self):
print('a')
class B(A):
def __init__(self):
print('b')
A.__init__(self)
class C(A):
def __init__(self):
print('c')
A.__init__(self)
class D(B,C):
def __init__(self):
print('d')
super().__init__()
test = D()
and it's output is d, b, a
after changing B and C's A.__init__() to super()
class A:
def __init__(self):
print('a')
class B(A):
def __init__(self):
print('b')
super().__init__() # THIS AND ^^
class C(A):
def __init__(self):
print('c')
super().__init__() # THIS ^^
class D(B,C):
def __init__(self):
print('d')
super().__init__()
test = D()
output is d, b, c, a
can someone explain why the program acts like that.
In order to understand super()
we have to understand the MRO concept.
MRO or Method Resolution Order
When a child's method is being called, there should be some inner mechanism to determine which method to invoke.
The MRO, defines the order in which base classes are searched in order to execute a method.
The method is being searched:
The MRO allows converting the graph of inheritance to a linear list to iterate over.
What Was Going On In The Code
In this version, class D
was defined:
class D(B,C):
def __init__(self):
print('d')
super().__init__()
Therefore when D
object has been created we witnessed 'd' as the first input.
Then in D
constructor a call to:
super().__init__()
The super()
method calls the next class in the MRO.
D
MRO is:
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
print(D.__mro__)
So the next class is B
therefore B
constructor has been called, and we witnessed B
as the second output.
Class B
constructor is:
class B(A):
def __init__(self):
print('b')
A.__init__(self)
So it directly calls class A constructor and we witness 'A' as the third and last output.
In this version class D
constructor stays the same as before, so the first output is 'd' again.
Then, as before B
constructor is being called, and 'b' is printed as the second output:
class B(A):
def __init__(self):
print('b')
super().__init__()
This time, however, super()
is being called again.
When looking at D
MRO (note that we are D
object in B
constructor, so the relevant MRO is of D
):
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
The next class is C
, so C
constructor is being invoked, and we witness 'c' as the third output:
class C(A):
def __init__(self):
print('c')
super().__init__()
Lastly, the super()
method is being called again, and following the MRO we end in class A
:
class A:
def __init__(self):
print('a')
The last output is 'a', and we finish.
Moreabout
Read more on super()
method in the docs.