Method Resolution Order in Python
Method Resolution Order, or MRO, is the order in which Python looks for a method or an attribute in a class hierarchy. This is particularly crucial in multiple inheritance to ensure consistency and predictability in the resolution of methods.
Python uses the C3 Linearization Algorithm to calculate the MRO. This algorithm ensures that:
- Child classes are checked before their parent classes.
- The inheritance order is maintained.
- No ambiguities occur even in complicated inheritance scenarios.
Viewing MRO
Python provides the __mro__ attribute and mro() method to inspect the MRO of a class.
Example:
class A:
pass
class B(A):
pass
print(B.__mro__)
# OR
print(B.mro())
Output:
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
Explanation:
- The MRO starts with
B, the class itself. - Then it moves to its parent
A, and finally the baseobjectclass, which is the root of all classes in Python.
MRO with super()
The super() function uses MRO to determine the next class in line to delegate method calls.
Example:
class A:
def method(self):
print("A method")
class B(A):
def method(self):
print("B method")
super().method()
b = B()
b.method()
Output:
B method
A method
Explanation:
- The
method()inBis called first. super().method()inBcalls themethod()ofA, as per the MRO.
Multiple Inheritance Example
When a class inherits from multiple parent classes, Python resolves the order using MRO.
Example:
class A:
def method(self):
print("A method")
class B(A):
def method(self):
print("B method")
class C(A):
def method(self):
print("C method")
class D(B, C):
pass
d = D()
d.method()
print(D.__mro__)
Output:
B method
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
Explanation:
Dinherits fromBandC.- MRO is:
D → B → C → A → object. - The
method()inBis called first becauseBappears beforeCinD(B, C).
Rules of C3 Linearization
1. Child Before Parent:
Always checks the child class before its parent classes.
2. Preserve Inheritance Order:
If a class inherits from several classes, then it follows the order defined by the inheritance tuple.
3. Avoid Conflicts:
The algorithm eliminates any possibility of ambiguity of method resolution; specifically, the case of diamond-shaped inheritance.
Real-World Example
Example:
class Animal:
def speak(self):
print("Animal speaks")
class Mammal(Animal):
def speak(self):
print("Mammal speaks")
class Bird(Animal):
def speak(self):
print("Bird chirps")
class Bat(Mammal, Bird):
pass
bat = Bat()
bat.speak()
print(Bat.__mro__)
Output:
Mammal speaks
(<class '__main__.Bat'>, <class '__main__.Mammal'>, <class '__main__.Bird'>, <class '__main__.Animal'>, <class 'object'>)
Explanation:
Batinherits from bothMammalandBird.- MRO:
Bat → mammal → bird → animal → object. - The
speak()method inMammalis invoked first because, in the MRO, Mammal appears beforeBird.
Diamond Inheritance
Example:
class A:
def method(self):
print("A method")
class B(A):
def method(self):
print("B method")
class C(A):
def method(self):
print("C method")
class D(B, C):
def method(self):
print("D method")
super().method()
d = D()
d.method()
print(D.__mro__)
Output:
D method
B method
C method
A method
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
Explanation:
- MRO resolves the diamond pattern without ambiguity:
D → B → C → A → object.
Key Takeaways
1. Deterministic MRO:
It always returns a definite order of resolving methods.
2. C3 Linearization ensures no conflicts:
Even in the diamond situation, MRO does not cause ambiguity.
3. super() simplifies delegation of methods:
You do not need to hardcode names of the parent classes because super() will automatically find the next class in MRO.
4. object is the ultimate base class:
All Python classes are subclasses of object. It is the last in the MRO.