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:

  1. Preserves Local Precedence: Classes listed first in the inheritance declaration (class D(B, C):) appear before classes listed later.
  2. Preserves Monotonicity: If class A precedes class B in parent MROs, A precedes B in 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__ End

Because 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!
Display Options
Appearance
Text Size
100%