Skip to content

Method Resolution Order (MRO)

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.

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
# \ /
# D
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
# 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.
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 correctly
class 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 once
  • 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 call super().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 order at class-definition time instead of picking an arbitrary, surprising order.
  • Check any class’s MRO directly with ClassName.__mro__ or ClassName.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’s super().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 class statement.
  1. 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?

    AnswerD, 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)`.
  2. How do you inspect a class’s actual method resolution order?

    Answer`ClassName.__mro__` (a tuple) or `ClassName.mro()` (a list).
  3. Why does super() matter for the MRO specifically, versus just calling ParentClass.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.