Python's __mro__
(Method Resolution Order) is a crucial concept that determines how Python looks up methods in inheritance hierarchies. Let's dive into what it is, why we need it, and see it in action.
🔍 What is mro?
__mro__
is a tuple that defines the order in which Python searches for methods and attributes in a class hierarchy. Every class has this attribute, showing its linearized inheritance path.
class Animal: pass class Dog(Animal): pass print(Dog.__mro__) # (<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>)
Python searches from left to right: first in Dog
, then Animal
, finally object
.
🎯 Why Do We Need It?
The MRO solves the diamond problem in multiple inheritance - when a class inherits from multiple classes that share a common ancestor.
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): # Diamond inheritance pass d = D() print(d.method()) # "B" - follows MRO print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
Without MRO, Python wouldn't know whether to call B.method()
or C.method()
. The MRO provides a consistent, predictable order using the C3 linearization algorithm.
💡 Practical Use Cases
1. Super() in Cooperative Inheritance
super()
follows the MRO, not just the parent class. This enables cooperative multiple inheritance:
class Logger: def save(self): print("Logging...") super().save() # Continues MRO chain class Database: def save(self): print("Saving to database...") class Model(Logger, Database): def save(self): print("Validating...") super().save() # Calls Logger.save() model = Model() model.save() # Output: # Validating... # Logging... # Saving to database...
2. Mixin Classes
MRO makes mixins powerful by ensuring methods are called in the right order:
class TimestampMixin: def save(self): self.updated_at = "2025-08-11" super().save() class ValidationMixin: def save(self): if not hasattr(self, 'name'): raise ValueError("Name required") super().save() class BaseModel: def save(self): print(f"Saving {self.name} at {self.updated_at}") class User(ValidationMixin, TimestampMixin, BaseModel): def __init__(self, name): self.name = name user = User("Alice") user.save() # Validates, adds timestamp, then saves # Output: Saving Alice at 2025-08-11
3. Framework Method Overriding
Web frameworks like Django use MRO to let you override specific behaviors while keeping others:
class BaseView: def get(self): return self.render() def render(self): return "Base rendering" class AuthMixin: def get(self): if not self.is_authenticated(): return "Login required" return super().get() def is_authenticated(self): return False class MyView(AuthMixin, BaseView): def render(self): return "Custom rendering" view = MyView() print(view.get()) # "Login required" - AuthMixin.get() runs first print(MyView.__mro__) # Shows: MyView -> AuthMixin -> BaseView -> object
🚀 Key Takeaways
-
__mro__
defines the method lookup order in inheritance hierarchies - It solves the diamond problem using C3 linearization
- Order matters in multiple inheritance:
class Child(Parent1, Parent2)
searches Parent1 before Parent2 -
super()
follows the MRO, not just the immediate parent - Understanding MRO is essential for designing effective mixin classes and using frameworks
Remember: when in doubt, check YourClass.__mro__
to see the exact lookup order!
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.