Method Resolution Order#
Method Resolution Order (MRO) is the deterministic sequence Python uses to find methods and attributes in classes — especially critical with multiple inheritance. Python uses the C3 linearization algorithm, which guarantees each class appears once, respects inheritance order, and stays monotonic. Misunderstanding MRO causes subtle bugs with super() and is a favorite interview topic.
This page completes the MRO preview in Introdicion to OOPs and Inheritance and Multiple Inheritance.
How to use this page
Read after inheritance basics. Work through the diamond examples with Class.__mro__ in a REPL. Related: Polymorphism, Abstract Base Classes.
At a glance
| Track | Python Advanced → Object Oriented Programming |
| Sections | 14 major topics |
| Outline | Use the right-hand TOC to jump |
Topics: Why MRO exists · Inspecting MRO · C3 linearization — intuitive rules · super() follows MRO, not the parent · super() with explicit arguments · Attribute lookup vs method lookup · Cooperative super() in __init__ · Mixins and MRO ordering · … (+6 more)
- Why MRO exists
- Inspecting MRO
- C3 linearization — intuitive rules
super()follows MRO, not the parentsuper()with explicit arguments- Attribute lookup vs method lookup
- Cooperative
super()in__init__ - Mixins and MRO ordering
- MRO with ABCs
- Comparison with other languages
- Debugging MRO issues
mro()dispatch visualization … and 2 more sections
Why MRO exists#
Without a strict order, multiple inheritance is ambiguous:
class A:
def method(self): return "A"
class B(A):
def method(self): return "B"
class C(A):
def method(self): return "C"
class D(B, C):
pass
D().method() # Which method? → B's, per MRO
Python must answer: which method does D().method() invoke? MRO provides a single linear order.
Inspecting MRO#
class D(B, C):
pass
D.__mro__
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
D.mro() # same as list(D.__mro__)
help(D) # shows MRO at top
| Tool | Output |
|---|---|
Class.__mro__ |
Tuple of classes in lookup order |
Class.mro() |
List version |
help(Class) |
Human-readable hierarchy |
Lookup rule: when accessing obj.method, Python searches type(obj).__mro__ in order, returning the first matching attribute.
C3 linearization — intuitive rules#
For class D(B, C):
- D comes first
- B before C (left-to-right in class statement)
- A (common ancestor) after both B and C
- object last
- No class appears twice
- If B comes before C in D's bases, B precedes C in MRO
Result: D → B → C → A → object
Merge intuition (formal C3)#
L(D) = D + merge(L(B), L(C), [B, C])
= D + merge([B, A, object], [C, A, object], [B, C])
= D + B + merge([A, object], [C, A, object], [C])
= D + B + C + merge([A, object], [A, object])
= D + B + C + A + object
You rarely compute this by hand — use Class.__mro__. Know that invalid hierarchies raise TypeError at class creation:
super() follows MRO, not the parent#
This is the most important MRO insight:
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
super().method()
class C(A):
def method(self):
print("C")
super().method()
class D(B, C):
def method(self):
print("D")
super().method()
D().method()
Output:
super().method() in B calls C.method, not A.method directly — because C is next in D.__mro__ after B.
super() with explicit arguments#
Two-argument form (useful outside methods):
Inside a method, zero-argument super() (Python 3) is equivalent to:
super().__thisclass__ # implicit in 3.x: super() in method
# CPython binds __class__ cell — super() uses current class and self
In Python 3, always prefer bare super() inside instance methods.
Classmethod super()#
class Base:
@classmethod
def setup(cls):
return [cls.__name__]
class Derived(Base):
@classmethod
def setup(cls):
return super().setup() + ["extended"]
Attribute lookup vs method lookup#
Same MRO applies to attributes:
class A:
x = "A"
class B(A):
x = "B"
class C(A):
x = "C"
class D(B, C):
pass
D.x # "B" — first in MRO
D().x # "B"
Instance attributes shadow class attributes regardless of MRO:
Cooperative super() in __init__#
Multiple inheritance requires every class to call super().__init__():
class Root:
def __init__(self):
print("Root")
class Left(Root):
def __init__(self):
super().__init__()
print("Left")
class Right(Root):
def __init__(self):
super().__init__()
print("Right")
class Combined(Left, Right):
def __init__(self):
super().__init__()
print("Combined")
Combined()
Output (MRO: Combined → Left → Right → Root → object):
Each super().__init__() invokes the next __init__ in MRO — Root runs once despite two parents.
Mixins and MRO ordering#
class LogMixin:
def log(self, msg: str):
print(f"[LOG] {msg}")
class CacheMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._cache = {}
class Service(LogMixin, CacheMixin):
def __init__(self, name: str):
super().__init__()
self.name = name
Service.__mro__
# Service, LogMixin, CacheMixin, object
Convention: list mixins left-to-right, most specific first, base class last:
MRO with ABCs#
ABCs participate like any class:
from abc import ABC, abstractmethod
class Readable(ABC):
@abstractmethod
def read(self) -> str: ...
class Writable(ABC):
@abstractmethod
def write(self, data: str) -> None: ...
class File(Readable, Writable):
def read(self) -> str:
return "data"
def write(self, data: str) -> None:
print(data)
File.__mro__ # File, Readable, Writable, ABC, object
Comparison with other languages#
| Language | MRO |
|---|---|
| Python 3 | C3 linearization |
| Python 2 (old-style) | Depth-first, left-to-right (problematic) |
| C++ | Virtual inheritance (complex, manual) |
| Java | Single inheritance only (interfaces separate) |
Python 3's C3 guarantees:
- Consistency — no ambiguous order
- Monotonicity — subclasses preserve parent order
- Local precedence — base order in class statement respected
Debugging MRO issues#
Symptom: super() skips expected class#
class A:
def f(self): print("A")
class B(A):
def f(self):
print("B")
super().f()
class C(A):
def f(self): print("C") # no super() — chain stops
class D(B, C):
def f(self):
print("D")
super().f()
D().f() # D, B, C — never reaches A because C doesn't call super()
Fix: every class in cooperative chain must call super().
Symptom: TypeError: Cannot create a consistent method resolution order#
Occurs when designing incompatible base orders — restructure inheritance.
mro() dispatch visualization#
flowchart LR
D --> B
D --> C
B --> A
C --> A
A --> O[object]
Lookup for D().method(): visit D → B → C → A → object.
Practical interview examples#
Which class's method runs?#
class X:
def who(self): return "X"
class Y(X):
def who(self): return "Y"
class Z(X):
def who(self): return "Z"
class W(Y, Z):
pass
W().who() # "Y"
W.__mro__ # W, Y, Z, X, object
super() return value chain#
class A:
def compute(self) -> int:
return 1
class B(A):
def compute(self) -> int:
return super().compute() + 10
class C(A):
def compute(self) -> int:
return super().compute() + 100
class D(B, C):
def compute(self) -> int:
return super().compute() + 1000
D().compute() # 1111 = 1000 + 110 + 1? Let's trace:
# D.compute: super() → B.compute → super() → C.compute → super() → A.compute
# = 1000 + (10 + (100 + 1)) = 1111
MRO and __slots__#
Subclasses must declare parent slots:
class Base:
__slots__ = ("x",)
class Child(Base):
__slots__ = ("y",) # implicit: ("x", "y") effective
Child.__slots__ # ("y",) only in __slots__ attr, but x works via Base
MRO affects which __dict__/__slots__ layout applies — advanced topic; know slots exist in inheritance.
Interview traps (quick reference)#
| Trap | What goes wrong | Safe approach |
|---|---|---|
Assuming super() = parent class |
Skips or wrong order in MI | Think "next in MRO" |
| Broken cooperative chain | Some classes never initialized | Every class calls super() |
| Wrong mixin order | Unexpected method wins | Mixins first, base last |
Hardcoding Parent.method(self) |
Bypasses MRO | Use super() |
| Computing MRO by hand incorrectly | Wrong interview answer | Use Class.__mro__ |
Class without super() in diamond |
Parent method runs multiple times or not at all | Cooperative pattern |
Mental model checklist#
- How do you inspect MRO for any class?
- What does
super().method()call in a diamond hierarchy? - Why must every class in a cooperative chain call
super()? - What causes
TypeError: Cannot create a consistent method resolution order? - How does left-to-right base order in
class D(B, C)affect MRO?
What's next#
| Topic | Page |
|---|---|
| Inheritance patterns | Inheritance and Multiple Inheritance |
| Polymorphism | Polymorphism |
| Class methods + MRO | Class Methods and Static Methods |
| Basics OOP | Introdicion to OOPs |