Method Resolution Order (MRO)
Method Resolution Order (MRO)
Section titled “Method Resolution Order (MRO)”What it is
Section titled “What it is”MRO is the order Python searches through a class’s ancestors when resolving an attribute or method — which matters most under multiple inheritance, where more than one parent could define the same name. CPython uses the C3 linearization algorithm, which guarantees a consistent order: a class always comes before its parents, and the order parents were listed in the class statement is preserved.
Before this feature
Section titled “Before this feature”Older single-inheritance-style languages (and Python 1.x’s original depth-first left-to-right search) could produce surprising or inconsistent results with “diamond” inheritance — where two parent classes share a common ancestor:
# A# / \# B C# \ /# Dclass A: def greet(self): return "A"
class B(A): def greet(self): return "B"
class C(A): def greet(self): return "C"
class D(B, C): pass
# With a naive depth-first search, D -> B -> A would find A.greet() before# ever checking C — even though C is also a direct parent of D and lists# after B. C3 linearization fixes this.After this feature
Section titled “After this feature”class A: def greet(self): return "A"
class B(A): def greet(self): return "B"
class C(A): def greet(self): return "C"
class D(B, C): pass
print(D.__mro__)# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
d = D()print(d.greet()) # 'B' -- B comes before C, and crucially, before A# super() follows the MRO, not "my direct parent" — this is what makes# cooperative multiple inheritance work correctlyclass B(A): def greet(self): return "B+" + super().greet()
class C(A): def greet(self): return "C+" + super().greet()
class D(B, C): def greet(self): return "D+" + super().greet()
print(D().greet()) # 'D+B+C+A' -- super() walks the whole MRO, calling each onceWhy this is better
Section titled “Why this is better”- Consistent, predictable order: C3 linearization guarantees every class appears before its parents and preserves the listed base-class order, avoiding the ambiguous results naive depth-first search could produce.
- Makes cooperative multiple inheritance actually work:
super()calls chain through the entire MRO (not just “my parent”), so mixins can each callsuper().method()and the whole chain runs exactly once per class — the basis of Python’s mixin pattern. - Fails loudly, not silently: if a class hierarchy has no consistent linearization possible, Python raises
TypeError: Cannot create a consistent method resolution orderat class-definition time instead of picking an arbitrary, surprising order.
Key notes / edge cases
Section titled “Key notes / edge cases”- Check any class’s MRO directly with
ClassName.__mro__orClassName.mro(). super()in Python 3 (no arguments needed) resolves relative to the MRO, not to “the literal parent class” — this is why mixins work: each class’ssuper().method()call hands off to the next class in the MRO, regardless of who its direct parent is.- The general rule of thumb: a class comes before its bases, and if a class inherits from multiple bases, they’re searched in the order listed in the
classstatement.
Quick practice
Section titled “Quick practice”-
For
class D(B, C)where both B and C inherit from A, in what order does Python search for a method D doesn’t define?Answer
D, then B, then C, then A, then `object` — B is fully searched before C, per the C3 linearization / the order listed in `class D(B, C)`. -
How do you inspect a class’s actual method resolution order?
Answer
`ClassName.__mro__` (a tuple) or `ClassName.mro()` (a list). -
Why does
super()matter for the MRO specifically, versus just callingParentClass.method(self)directly?Answer
`super()` follows the *computed MRO* for the actual instance's class, so in a diamond/mixin hierarchy each ancestor's method runs exactly once in the correct order — calling `ParentClass.method(self)` directly bypasses the MRO and can call a class twice or skip one entirely.