Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python diamond inheritance and the use of super() in the parents of the derived class

First of all, I have to apologize for not having a better title. Feel free to change it if you find a more appropriate one.

Basically, I have been bugged by the behaviour of Python's multiple inheritance. In my previous SO question, I got directed to read Python's C3 MRO. That really helps me to have a better understanding of multiple inheritance in Python. Just when I thought I got the grasp of it, I bumped into the following scenario, which I can't seem to make sense of.

class UltimateBase(object):
    def check(self):
        print 'base check'

class AMixin1(UltimateBase):
    def check(self):
        print 'check A'

class BMixin1(UltimateBase):
    def check(self):
        print 'check B'

class CMixin1(UltimateBase):
    def check(self):
        print 'check C'

class AMixin2(UltimateBase):
    def check(self):
        print 'check A'
        return super(AMixin2, self).check()

class BMixin2(UltimateBase):
    def check(self):
        print 'check B'
        return super(BMixin2, self).check()

class CMixin2(UltimateBase):
    def check(self):
        print 'check C'
        return super(CMixin2, self).check()

class MyView1(AMixin1, BMixin1, CMixin1):
    pass

class MyView2(AMixin2, BMixin2, CMixin2):
    pass

class MyView3(AMixin1, BMixin2, CMixin2):
    pass

class MyView4(AMixin2, BMixin1, CMixin2):
    pass

class MyView5(AMixin2, BMixin2, CMixin1):
    pass

class MyView6(AMixin1, BMixin1, CMixin2):
    pass

class MyView7(AMixin1, BMixin2, CMixin1):
    pass

class MyView8(AMixin2, BMixin1, CMixin1):
    pass

myview1 = MyView1()
myview2 = MyView2()
myview3 = MyView3()
myview4 = MyView4()
myview5 = MyView5()
myview6 = MyView6()
myview7 = MyView7()
myview8 = MyView8()

myview1.check()
print '------------------------'
myview2.check()
print '------------------------'
myview3.check()
print '------------------------'
myview4.check()
print '------------------------'
myview5.check()
print '------------------------'
myview6.check()
print '------------------------'
myview7.check()
print '------------------------'
myview8.check()
print '------------------------'

Outputs:

check A
------------------------
check A
check B
check C
base check
------------------------
check A
------------------------
check A
check B
------------------------
check A
check B
check C
------------------------
check A
------------------------
check A
------------------------
check A
check B
------------------------

I can trace out a pattern based on observing the outputs, but it bugs me not understanding the rationale behind this result.

I have questions like, for example, why does myview2.check() return

check A
check B
check C
base check

not

check A
base check

It seems to me that I am missing a key piece about multiple inheritance. Please fill in the gap for me.

like image 963
tamakisquare Avatar asked Apr 05 '12 18:04

tamakisquare


People also ask

What is the use the super () function in Python inheritance?

The super() function is used to give access to methods and properties of a parent or sibling class. The super() function returns an object that represents the parent class.

What is diamond inheritance in Python?

The diamond problem is an ambiguity that is arisen when there are two classes say B and C that inherit / are derived from a single class A, and there is another class D, that is a class derived from multiple inheritance and inherits from B as well as C.

How does super () method play an important role in inheritance?

Python | super() in single inheritance. At a fairly abstract level, super() provides the access to those methods of the super-class (parent class) which have been overridden in a sub-class (child class) that inherits from it.

Can we use super in multiple inheritance in Python?

Supercharge Your Classes With Python super()Python supports inheritance from multiple classes.


2 Answers

When you call myview2.check(), it traverses siblings then calls the base class. Whenever one of those traversals hits AMixin1, BMixin1, or CMixin1, it stops, because those classes do not call super(..., self).check().

As Benn points out, this is described in the official Python documentation. And if you think about it, it pretty much has to work this way. Subclasses are going to assume the base class method isn't called before the subclass calls super(). If it is (or, worse, if it depends on the order its siblings are listed), it makes things very difficult to deal with.

like image 169
Chris B. Avatar answered Oct 26 '22 23:10

Chris B.


Tried to get my head round the same question. I found the following code helped simplify the issue:

(Basically, when all the supers have been hit for a class, that class gets called.)

class A:
  def f(self):
    print("............A: the common base of B and C")

class B(A):
  def f(self):
    print("........B: the left base of D")
    super().f()

class C(A):
  def f(self):
    print("........C: the right base of D")
    super().f()

class D(B,C):
  def f(self):
    print("....D: the top class")
    super().f()

d = D()

d.f()

Output:

....D: the top class
........B: the left base of D
........C: the right base of D
............A: the common base of B and C

Try Online

like image 44
c z Avatar answered Oct 26 '22 22:10

c z