I'm a newbie for MRO and having problem figuring out the logic for these outputs.
class A(object):
def save(self):
print "A"
class B(A):
def save(self):
print "B"
super(B, self).save()
class C(A):
def save(self):
print "C"
super(C, self).save()
class D(C, B):
def save(self):
print "D"
super(D,self).save()
D().save()
Output:
D
C
B
A
My question here is how super(C)
is calling B.save()
.
As per MRO: super(C, self)
is not about the "base class of C
", but about the next class in the MRO list of C
. But there is no B
in MRO list of C
.
class A(object):
def save(self):
print "A"
class B(A):
def save(self):
print "B"
super(B, self).save()
class C(A):
def save(self):
print "C"
# removed super call here
class D(C, B):
def save(self):
print "D"
super(D,self).save()
D().save()
Output:
D
C
class A(object):
def save(self):
print "A"
class B(object):
#inherits object now instead of A
def save(self):
print "B"
super(B, self).save()
class C(A):
def save(self):
print "C"
super(C, self).save()
class D(C, B):
def save(self):
print "D"
super(D,self).save()
D().save()
Output:
D
C
A
How is the MRO affected if B
is not inheriting from A
, but object
directly?
Can someone explain the reason behind this?
The Diamond Problem is an ambiguity that arises in multiple inheritance when two parent classes inherit from the same grandparent class, and both parent classes are inherited by a single child class.
In Python as all classes inherit from object, potentially multiple copies of object are inherited whenever multiple inheritance is used. That is, the diamond problem occurs even in the simplest of multiple inheritance.
The "diamond problem" (sometimes referred as the "deadly diamond of death") is the generally used term for an ambiguity that arises when two classes B and C inherit from a superclass A, and another class D inherits from both B and C.
The ambiguity of the diamond problem does not exist in python since we have the __mro__ attribute -- Method Resolution Order. Python has the super() function which is used to avoid the use of the base class or the parent class explicitly.
In order to tackle these questions, you need to, first, understand how do MRO and super() work.
MRO Python Document - MRO
In Python, MRO is based on C3 linearization. (There are many examples in wiki and python documents)
super() (check out Python Document - super())
According to the Python Document, it says..
Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr() except that the type itself is skipped.
Therefore, super() will search based on MRO (classobject.__mro__
) and it will be satisfied by either parent class or sibling class of type.
Back to your cases,
Case 1D.__mro__ = (D, C, B, A, object)
Thus, the output will be D C B A
Case 2
The MRO of D is as the same as D in case 1. ((D, C, B, A, object)
)
However, super() will stop, or be satisfied, at C because save()
in class C does not have a super function call implemented.
Case 3
The MRO of D is equal to (D, C, A, B, object)
. Therefore, when super() reached save() function in class A, it would consider satisfied. That's the reason why save function in B had never been called.
Moreover, if you switch the inheritance sequence of D from C, B to B, C. (class D(C, B)
to class D(B, C)
). Then, when you call D().save()
, the output will be D B because D.__mro__ = (D, B, C, A, object)
and super() will be satisfied at class B.
Furthermore, from my understanding, super() is not a function that forces child class object to call all override functions in parent classes. If you want to force your class to call all override functions in its parent classes. You could try to do something like:
class Base(object):
def method(self):
print('Base-method')
class Parent(object):
def method(self):
print('Parent-method')
class Child(Parent):
def method(self):
print('Child-method')
super(Child, self).method()
class GrandChild(Child, Base):
def method(self):
print('GrandChild-method')
for base in GrandChild.__bases__:
base.method(self)
#super(GrandChild, self).method()
Then, when you call `GrandChild().method(), the output will be:
GrandChild-method
Child-method
Parent-method
Base-method
Note:GrandChild.__bases__
contains only class Child and class Base
PS.
By saying satisfied above, I meant there is no more super() calls.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With