MRO: Method Resolution Order (C3 linearization)

RoadmapsPython

Overview

Method Resolution Order (`MRO`) is the sequence in which Python looks for a method or attribute in a hierarchy of classes. Think of `MRO` like a search path for documents: if a document isn't in your desk, you check the department filing cabinet, then the company archive, strictly following a designated path so everyone finds the same document. You can inspect the resolution sequence of any class using two approaches: - `cls.__mro__`: A special attribute that returns a `tuple` of the classes in resolution order. Raises `AttributeError` if accessed on an instance rather than a class. - `cls.mro()`: A class method that computes and returns a `list` of the classes in resolution order. Raises `AttributeError` if called on an instance. Python uses the C3 linearization algorithm to guarantee a consistent lookup order. This algorithm enforces three strict rules for building the `MRO`: - Children precede parents: A subclass is always checked before its base classes. - Left-to-right processing: Multiple parent classes are checked in the exact order they are listed in the class definition. - Monotonicity: A class is never visited until all of its subclasses have been visited. class BaseUser: pass class Admin(BaseUser): pass class SuperUser(BaseUser, Admin): pass # TypeError: Cannot create a consistent method resolution This is the inconsistent `MRO` trap. Because `Admin` inherits from `BaseUser`, `Admin` must be checked first, but `class SuperUser(BaseUser, Admin)` explicitly requests checking `BaseUser` before `Admin`. Using `MRO` to properly manage `super()` calls is covered fully in `method-overriding`.

It prevents ambiguity in multiple inheritance (the `diamond problem`) by defining a clear, predictable path for attribute lookup.

Where used: Django, Pydantic, FastAPI

Why learn this

Code walkthrough

class A:
    def ping(self):
        print('A')

class B(A):
    def ping(self):
        print('B')

class C(A):
    def ping(self):
        print('C')

class D(B, C):
    pass

d = D()
d.ping()

Focus: class D(B, C): # B is listed before C, so D's MRO checks B before C

Aha moment

class Top:
    name = 'Top'

class Left(Top):
    pass

class Right(Top):
    name = 'Right'

class Bottom(Left, Right):
    pass

print(Bottom.name)

Prediction: What does `Bottom.name` print?

Common guess: `Top`, because `Left` is the first parent and it inherits from `Top`.

Python's `MRO` checks `Right` before `Top`. Even though `Left` is the first parent, `Top` is a shared base class, so Python delays checking `Top` until all subclasses (like `Right`) are checked.

Common mistakes

Glossary

linearization algorithm
A mathematical set of rules Python uses to flatten a complex family tree of classes into a single, straightforward list, e.g. `D.__mro__` shows the resolved order for class `D`.
diamond problem
An ambiguity that arises when a class inherits from two different classes that both share the same base class, e.g. `class D(B, C)` where both `B` and `C` inherit from `A`.

Recall questions

Understanding checks

What is the output of this code?

B

`class D` inherits from `B` and `C` (in that order). Following the Method Resolution Order (`MRO`), Python checks `D`, then `B`. Since `name` is found in `B`, it prints `B` without ever checking `C` or `A`.

Why does Python's C3 linearization algorithm prevent ambiguity in the `diamond problem`?

It guarantees a consistent lookup order where subclasses are always checked before their base classes, and multiple parent classes are checked in the left-to-right order they are specified.

This deterministic set of rules ensures that developers always know exactly which inherited method or attribute will be executed, removing any ambiguity from complex multiple inheritance structures.

What happens when this code is executed?

It raises an AttributeError.

The `__mro__` attribute is only available on the class itself (`App`), not on instances of the class (`app`). Attempting to access it on an instance raises an `AttributeError`.

Practice tasks

Check the MRO

Modify the starter code to print the Method Resolution Order (MRO) of the `Combined` class using either the `__mro__` attribute or `.mro()` method.

Challenge

Trigger an MRO Error

Create a class hierarchy that causes a `TypeError: Cannot create a consistent method resolution` when defined. You need at least three classes to create an inconsistent inheritance graph.

Continue learning

Previous: Class Creation and Instantiation

Next: Method Overriding

Return to Python Roadmap