5
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?

alex_noname
  • 26,459
  • 5
  • 69
  • 86
Stupid Man
  • 885
  • 2
  • 14
  • 31
  • 2
    Check ```grandchild.__mro__```. – Henry Tjhia Nov 08 '20 at 16:10
  • @HenryTjhia I have printed the MRO for this here: https://ideone.com/dhbhvT What I dont understand is, how and why is the code jumping from line 16 to line 24. – Stupid Man Nov 08 '20 at 16:16
  • The result should be self-explainatory. Basically ```super``` routing through that ```tuple``` result. – Henry Tjhia Nov 08 '20 at 16:19
  • 1
    @HenryTjhia On line 17, when `super().form1()` is called, it calls `child2.form1()` because in the MRO hierarchy, `child2` is the parent (super() in child1 will return child2) of `child1`. Is this correct? – Stupid Man Nov 08 '20 at 16:28
  • Why would ```child2``` become parent of ```child1```? Both ```child1``` and ```child2``` are parent of ```grandchild```, which in turn have ```parent``` as their ```superclass```. ```parent``` itself has implied ```object``` as their ancestor. You are calling method through instance of ```grandchild``` as consequence should follow ```grandchild``` ```mro``` pattern. – Henry Tjhia Nov 08 '20 at 16:39
  • @HenryTjhia In the link I posted in my earlier comment, what is returned by `super()` on line 17? Is this line executed? If yes, then why dont I see line 6 being printed since `parent` is the super class of `child1`? – Stupid Man Nov 08 '20 at 17:22
  • Because after ```child1``` call, the next executed method is ```form1``` in the ```child2``` then stop because there is no any ```super``` remaining! If you want the parent's method also executed, you can do so by adding a ```super``` call in the ```form1``` of ```child2```. – Henry Tjhia Nov 08 '20 at 17:27
  • @HenryTjhia 1) `child1` and `child2` are parents of `grandchild`. 2) `parent` is parent of both `child1` and `child2` and grandparent of `grandchild`. Now when I call `callchildform1` on `g` which is instance of grandchild, MRO first looks for this method in `grandchild`, since theres no method of this name, it looks in its parent which is `child1`. There the method is found and it starts executing it, starting line 15. On line 17, `child1` calls `super.form1()` but since parent of `child1` is `parent` class, shouldnt line 17 jump to `form1` method in `parent` class? – Stupid Man Nov 08 '20 at 17:40

1 Answers1

4

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.

alex_noname
  • 26,459
  • 5
  • 69
  • 86