C.W.K.
Stream
Lesson 01 of 05 · published

Multiple Inheritance and the MRO

~22 min · mro, multiple-inheritance, c3, super

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

Multiple inheritance — the deep end

Python lets a class inherit from more than one parent. class C(A, B): is legal. The question that immediately arises: if both A and B define method(), which one wins on C().method()? The answer is the method resolution order (MRO) — a deterministic linearization of the inheritance graph computed by the C3 algorithm.

The C3 algorithm — the rule, not the proof

You don't need to memorize the algorithm. The rule it produces, in plain English: a class comes before all its parents, and parents appear in the order you wrote them. Python combines these constraints across the whole tree to get a single ordering. Cls.__mro__ shows it. If the constraints can't be satisfied, you get a TypeError at class definition time.

super() in multiple inheritance — the cooperative call

In single inheritance, super().method() calls the parent's method. In multiple inheritance, it calls the next class in the MRO, not necessarily the parent. This is "cooperative multiple inheritance" — every class in a diamond can call super() and the calls walk the MRO in order, hitting each class once.

Principle: When you mix multiple inheritance and super(), every class in the chain must accept and forward **kwargs. If one class forgets, the chain breaks. This is the cost of cooperative multiple inheritance and a major reason why most Python code prefers composition.

The diamond — and why MRO is non-trivial

The classic case: D inherits from B and C, both of which inherit from A. Without MRO, calling D().method() would risk calling A.method twice. C3 ensures every ancestor appears exactly once in the MRO, in an order consistent with all the local parent orderings.

Code

Inspecting the MRO·python
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

for cls in D.__mro__:
    print(cls.__name__)
# D
# B
# C
# A
# object

# Same as the ordered list:
print([c.__name__ for c in D.mro()])
Method resolution in action·python
class A:
    def name(self): return "A"

class B(A):
    def name(self): return "B"

class C(A):
    def name(self): return "C"

class D(B, C):
    pass

print(D().name())          # 'B'   — first in MRO after D
Cooperative super() — every class forwards·python
class A:
    def __init__(self, **kwargs):
        print("A.__init__")
        super().__init__(**kwargs)

class B(A):
    def __init__(self, **kwargs):
        print("B.__init__")
        super().__init__(**kwargs)

class C(A):
    def __init__(self, **kwargs):
        print("C.__init__")
        super().__init__(**kwargs)

class D(B, C):
    def __init__(self, **kwargs):
        print("D.__init__")
        super().__init__(**kwargs)

D()
# D.__init__
# B.__init__
# C.__init__
# A.__init__
# Each class hit ONCE, in MRO order, thanks to super()
Inconsistent inheritance — Python catches it·python
class A: pass
class B(A): pass
class C(A): pass

# This works — D's parents are (B, C); both have A as their parent.
class D(B, C): pass

# This DOESN'T — the parent orders contradict each other
try:
    class Bad(B, C, B):
        pass
except TypeError as e:
    print("MRO conflict:", e)

External links

Exercise

Build a class hierarchy: Vehicle, Wheeled(Vehicle), Engined(Vehicle), Car(Wheeled, Engined). Each class should define __init__(**kwargs) that prints its class name and calls super().__init__(**kwargs). Construct a Car() and observe the MRO order. Then print Car.__mro__ and verify the order matches what you saw at construction. Add an attribute on each class's __init__ so the final Car() instance has all four attributes set.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.