Multiple Inheritance, MRO & super() Mechanics
Python supports Multiple Inheritance, allowing a class to inherit from multiple parent classes. To resolve method lookups deterministically without falling into the Diamond Problem deadlock, CPython uses the C3 Linearization Algorithm to compute a linear Method Resolution Order (MRO) tuple.
This chapter details the Diamond Problem, the C3 Linearization algorithm, cooperative super() dispatch mechanics, and super() stack frame inspection.
1. The Diamond Problem & C3 Linearization Algorithm
The classic Diamond Problem occurs when a class D inherits from parent classes B and C, which both inherit from a common base class A:
The Diamond Problem Architecture:
[ Class A ]
/ \
[ Class B ] [ Class C ]
\ /
[ Class D ]If both B and C override a method foo(), in what order should D.foo() search parent classes?
C3 Linearization:
Python uses the C3 Linearization algorithm to construct a single deterministic sequence of classes (the __mro__ tuple). C3 guarantees two invariants:
- Preserves Local Precedence: Classes listed first in the inheritance declaration (
class D(B, C):) appear before classes listed later. - Preserves Monotonicity: If class
Aprecedes classBin parent MROs,AprecedesBin all subclass MROs.
Inspect any class’s MRO using Class.__mro__:
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)2. Cooperative super() Dispatch
super() in Python is not a reference to “the parent class”.
super()returns a proxy object that delegates method calls to the NEXT class in the instance’s MRO tuple.
class Base:
def __init__(self):
print("Base __init__")
class B(Base):
def __init__(self):
print("B __init__ Start")
super().__init__() # Calls NEXT class in instance's MRO!
print("B __init__ End")
class C(Base):
def __init__(self):
print("C __init__ Start")
super().__init__() # Calls NEXT class in instance's MRO!
print("C __init__ End")
class D(B, C):
def __init__(self):
print("D __init__ Start")
super().__init__()
print("D __init__ End")
d = D()Output Sequence:
D __init__ Start
B __init__ Start
C __init__ Start <-- Note: B's super() called C.__init__(), NOT Base.__init__()!
Base __init__
C __init__ End
B __init__ End
D __init__ EndBecause D’s MRO is (D, B, C, Base, object), calling super().__init__() inside B delegates to C (the next class in D’s MRO), establishing cooperative multiple inheritance.
3. Parameter Alignment in Cooperative super()
For cooperative super() to work seamlessly across multiple classes, all __init__ methods in the cooperative chain should accept *args, **kwargs to absorb extra arguments passed down the MRO sequence.
4. Inconsistent Hierarchy Errors (TypeError: Cannot create a consistent method resolution order)
If an inheritance declaration violates C3 Linearization (e.g. attempting to create a child class whose parent MRO order contradicts an ancestor’s MRO), CPython’s class creation phase fails instantly with a TypeError.
class X: pass
class Y: pass
class A(X, Y): pass
class B(Y, X): pass
# IMPOSSIBLE MRO: A requires X before Y; B requires Y before X!
class C(A, B): pass
# RAISES TypeError: Cannot create a consistent method resolution order (MRO) for bases X, Y!