The method resolution order used is a C3 linearization algorithm, and super
itself uses this MRO. The resolution for a type is obtained with the mro()
method, and cached in the __mro__
attribute:
>>> D.__mro__
(__main__.D, __main__.B, __main__.C, __main__.A, object)
>>> D.mro()
[__main__.D, __main__.B, __main__.C, __main__.A, object]
Since you call print after calling super, you'll see the reverse of this, i.e. A -> C -> B -> D.
I wonder how is class C being executed in this code flow when none of the flow calls it.
D.fun2
doesn't exist, so obj.fun2()
gets resolved (via the MRO) on B
instead:
>>> obj = D()
>>> obj.fun2
<bound method B.fun2 of <__main__.D object at 0x7fffe89a3cd0>>
Then in B.fun2
, the C.fun1
gets called here:
class B(A):
def fun2(self):
super().fun1() # <-- this resolves to C.fun1, not A.fun1!
print("This is class B")
C.fun1
is called since it's D's MRO which is active here, not B's, because the type(self) is D
. Note that super()
does not always resolve on the parent class, it can be resolved on a sibling class like in your example. Python's implementation was more or less lifted from another programming language called Dylan, where super
was named next-method
, a less confusing name in my opinion.