How does inheritance and Method Resolution Order (MRO) work in Python?

7 minadvancedoopinheritancemro

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:

  1. Local precedence order — a class appears before its own parents.
  2. 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."