Recently, I hit into an interesting issue with python multiple inheritance.
Initially, I have a linear inheritance hierarchy
A -> B -> C
class A(object): def f(self): print('A') class B(A): def f(self): super().f() print('B') class C(B): pass
A B (line break is ignored).
This works well but what if we have multiple equivalent classes of
and each of them needs a corresponding
class A1(object): def f(self): print('A1') class A2(object): def f(self): print('A2')
C1().f() should give
A1 B, and execution of
C2().f() should give
One straightforward implementation is to apply the linear inheritance multiple times, i.e.,
A1 -> B1 -> C1 A2 -> B2 -> C2
B2 have basically the same code, only differing in their base class.
This is obviously not a good design since the logic of
B is not reused.
A better solution is to use multiple inheritance, i.e.,
class C1(B0, A1): pass class C2(B0, A2): pass
B’s functionality and is to be defined later.
The class hierarchy looks like this
B0 is as follows
class B0(object): def f(self): super().f() print('B')
C1.f() indeed gives the desired result
Although it works, note that
B0 does not inherit directly from any of the
It only knows about the
As via the
One natural question is then
- How does
B0.f()access the corresponding
The core of this question is the order of function override in a complicated inheritance hierarchy, commonly known as Method Resolution Order (MRO).
According to python 3.6 doc,
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.
The mro attribute of the type lists the method resolution search order used by both getattr() and super(). The attribute is dynamic and can change whenever the inheritance hierarchy is updated.
In our case,
C1.__mro__ is given by
[<class '__main__.C1'>, <class '__main__.B0'>, <class '__main__.A1'>, <class 'object'>]
B0.f() correctly finds the desired function
There is a very nice document on the python MRO written by Dr. Michele Simionato with many examples.
One final thing to note is that new style classes (the ones inherit from
object) and classic classes use different MRO
- new style classes MRO: C3 linearization
- classic classes MRO: depth first and then left to right