How Method Resolution Order (MRO) is working in this Python code

class parent:
    def __init__(self):
        self.a=2
        self.b=4
    def form1(self): 
        print("calling parent from1")
        print('p',self.a+self.b)
 
class child1(parent):
    def __init__(self):
        self.a=50
        self.b=4
    def form1(self):
        print('bye',self.a-self.b)
    def callchildform1(self):
        print("calling parent from child1")
        super().form1()
 
class child2(parent):
    def __init__(self):
        self.a=3
        self.b=4
    def form1(self):
        print('hi',self.a*self.b)
    def callchildform1(self):
        print("calling parent from child2")
        super().form1()
 
class grandchild(child1,child2):
    def __init__(self):
        self.a=10
        self.b=4
    def callingparent(self):
        super().form1()
 
g=grandchild()
g.callchildform1()

In the above code, when I call g.callchildform1(), according to MRO rule, the method will be first searched in the same class, then the first parent (child1 here) and then the second parent (child2). As expected, it calls child1.callchildform1() and executes the first line print("calling parent from child1"). But after this, I expected that the next line super().form1() will be executed which will call parent.form1() but this doesnt happen. Instead, child2.form1() is being called. Please explain why this happens?

Upvotes: 5

Views: 1478

Answers (1)

alex_noname
alex_noname

Reputation: 32153

The documentation has a great explanation of how super() works:

super([type[, object-or-type]])

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 object-or-type determines the method resolution order to be searched. The search starts from the class right after the type.

For example, if __mro__ of object-or-type is D -> B -> C -> A -> object and the value of type is B, then super() searches C -> A -> object.

super() is equivalent to the expression super(__class__, self) where __class__ is an class object within whose method the super() is called. For example, super() within grandchild.callingparent(self) is essentially super(grandchild, self). And super() inside child1.callchildform1(self) function is super(child1, self).

MRO for grandchild is (<grandchild>, <child1>, <child2>, <parent>, <object>). Therefore, according to the above excerpt from documentation, when super().form1() is called in child1.callchildform1(), which is equivalent to super(child1, self), the search for the form1 method starts from the class right after the child1 in the MRO sequence and the first matching class with form1 method is child2.

This occurs due to that you are using the diamond inheritance structure and the principles that underlie the MRO:

with multiple inheritance hierarchies, the construction of the linearization is more cumbersome, since it is more difficult to construct a linearization that respects local precedence ordering and monotonicity.

When designing such hierarchies, it is need to follow the approach of cooperative inheritance, an explanation can be found, in this already classic article.

Upvotes: 4

Related Questions