Method Resolution Order (MRO)
Method Resolution Order (MRO) and C3 Linearization in Python
In complex object-oriented systems with multiple inheritance, determining which parent version of a method executes is not always trivial. Python resolves method lookups deterministically using the Method Resolution Order (MRO), calculated via the sophisticated C3 Linearization Algorithm. Understanding MRO is essential for mastering cooperative multiple inheritance, mixins, and large Python frameworks.
1. The Diamond Problem
The classic dilemma in multiple inheritance is the Diamond Problem:
+------------------+
| Class A |
| def action() |
+------------------+
/ \
/ \
+------------------+ +------------------+
| Class B | | Class C |
| def action() | | def action() |
+------------------+ +------------------+
\ /
\ /
+------------------+
| Class D |
| (B, C) |
+------------------+If Class D inherits from both B and C, and all three define action(), which method should execute when calling D().action()?
- In naive Depth-First Search (used in Python 2.1),
Awould be visited beforeC, meaningA's outdated base method could overrideC's specialized method! - Python solves this with C3 Linearization, ensuring that derived classes always precede their ancestors.
2. Inspecting the MRO
You can inspect the linear resolution order of any class using two attributes:
ClassName.__mro__: Returns a tuple of classes in resolution order.ClassName.mro(): Returns the same order as a list.
Python searches classes strictly from left to right along this sequence: D $\rightarrow$ B $\rightarrow$ C $\rightarrow$ A $\rightarrow$ object.
3. The Mathematics of C3 Linearization
The C3 algorithm guarantees three fundamental properties:
- 1Local Precedence Order: In
class D(B, C), classBis always evaluated before classC. - 2Monotonicity: If class
Xprecedes classYin the MRO of any parent class,Xwill precedeYin the MRO of any derived class. - 3Single Appearance: Each ancestor class appears exactly once in the MRO list.
The Linearization Formula:
$$L[C(B_1, B_2, \dots, B_N)] = C + \text{merge}(L[B_1], L[B_2], \dots, L[B_N], (B_1, B_2, \dots, B_N))$$
The merge operation inspects the head of each candidate list. If a head class is not present in the tail (any position other than the first) of any other list, it is extracted and appended to the MRO output.
4. super() is NOT "Call Parent"—It is "Call Next in MRO"!
One of the most widespread misconceptions in Python is that super() simply calls the direct parent class.
super() actually calls the NEXT class in the active MRO sequence of the originating instance!
Notice how super() in class B actually calls class C (its sibling!), not its parent A:
Output:
A's call to super().ping() invoked B.ping()! This cooperative dispatch pattern enables multi-tiered mixin architectures without hardcoded dependencies.
5. Inconsistent MRO: When Python Refuses to Compile
If an inheritance hierarchy violates the local precedence rules, the C3 merge algorithm fails, and Python refuses to create the class with a TypeError:
Multiple Choice Questions
1. Which algorithm does Python use to compute the Method Resolution Order (MRO)?
A. Breadth-First Search (BFS) B. C3 Linearization C. Dijkstra's Shortest Path D. A Search Answer: B Explanation:* Python 2.3+ utilizes the C3 Linearization algorithm to produce a deterministic, monotonic method resolution order.
2. In class D(B, C): pass, what does D.__mro__ return?
A. A list of all variable names in D B. A tuple showing the exact linear order in which classes are searched for methods and attributes C. A dictionary of parent class methods D. A string with the class name Answer: B Explanation: __mro__ returns a tuple representing the linear resolution sequence starting from the class itself down to object.
3. What does super() actually invoke in Python?
A. Strictly the first parent class declared in the class definition B. The next class in the active instance's Method Resolution Order (MRO) C. The root object class D. A global fallback function Answer: B Explanation: super() is dynamic; it searches the MRO of the instance currently executing and invokes the subsequent class in that chain.
4. What happens if a class definition creates an inheritance conflict that violates C3 linearity?
A. Python uses the first class and silently ignores the second B. Python raises a TypeError: Cannot create a consistent method resolution order (MRO) at class definition time C. The program hangs in an infinite loop D. Python runs in single inheritance mode Answer: B Explanation: If an inheritance graph is topologically inconsistent, Python detects the failure during class creation and raises a TypeError.
5. In the diamond inheritance class D(B, C) where both B and C inherit from A, why does C execute before A in the MRO?
A. Because C is shorter alphabetically B. Because C3 linearization guarantees that subclasses always precede their base ancestor classes C. Because Python flips class orders backwards D. Because A is marked as abstract Answer: B Explanation: C3 linearization guarantees that a parent class (A) is never visited until all of its derived subclasses (B and C) in the hierarchy have been evaluated.
Multiple Inheritance
Continue learning with hands-on practice, examples, and exercises in the upcoming topic.
Related Lessons
| Previous Lesson | Next Lesson |
|---|---|
| Property Decorator | Multiple Inheritance |
Practice Quiz
Test your understanding of this lesson with 5 questions. Each question has one correct answer.