How does inheritance and Method Resolution Order (MRO) work in Python?
Quick Answer
Python supports multiple inheritance, and the **MRO** determines the order classes are searched for an attribute/method. Since Python 2.3, MRO is computed with the **C3 linearization** algorithm, which merges each parent's own MRO plus the list of parents, preserving a consistent left-to-right, depth-first-but-deduplicated order. Inspect it with `ClassName.__mro__` or `ClassName.mro()`.
Detailed Answer
What MRO decides
When you access obj.attr and multiple base classes could define attr,
the MRO is the order Python searches classes to find it. For single
inheritance it's obvious (child, then parent, then grandparent...); with
multiple inheritance, it's not — and Python needs a deterministic rule.
class A:
def who(self):
return "A"
class B(A):
def who(self):
return "B"
class C(A):
def who(self):
return "C"
class D(B, C):
pass
D().who() # 'B'
D.__mro__
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
Naive depth-first search (D → B → A → C → A) would visit A twice and
find B's who before C's only by luck of ordering. C3 linearization
guarantees a single, consistent order: D, B, C, A, object.
C3 linearization, in plain terms
C3 merges the MROs of each parent with the list of parents itself, subject to two rules:
- Local precedence order — a class appears before its own parents.
- Monotonicity — if a class appears before another in a parent's MRO, it must appear before it in the child's MRO too.
The algorithm repeatedly takes the head of the first list that doesn't
appear in the tail of any other list — this is what guarantees D's bases
(B then C, in the order declared) are respected while still visiting
each ancestor exactly once.
Why this makes cooperative super() calls safe
Because the MRO is a single linear order shared by every class in the
hierarchy, calling super().method() inside B doesn't go straight to
A — it goes to whatever is next in D's MRO after B, which is C.
This is what makes "cooperative multiple inheritance" (mixins) work: each
class's super() call passes control along the same MRO, so every class
in the chain gets a turn, in a consistent order, regardless of which
concrete subclass you instantiate.
Inconsistent hierarchies raise an error
class X(A, B): pass
class Y(B, A): pass
class Z(X, Y): pass # TypeError: Cannot create a consistent MRO
If two base classes disagree on relative ordering, C3 has no valid linearization and Python refuses to create the class rather than guessing.
Interview-ready summary: MRO is the deterministic search order for
methods/attributes across a (possibly multiple-inheritance) class
hierarchy, computed by C3 linearization and inspectable via __mro__. It
guarantees each ancestor is visited once, respects declared base-class
order, and is what makes super() reliably cooperative rather than
hard-coded to "my direct parent."