11.1 Method Resolution Order
Method Resolution Order (MRO) determines the sequence in which Python looks for methods and attributes in the class hierarchy. This is especially important when dealing with multiple inheritance, where a class might inherit attributes and methods from several parent classes.
Basically, there's a strict fixed order (or rather, an algorithm), according to which Python navigates the inheritance tree. This algorithm ensures the correct order of method lookup, which can be described as follows:
The C3 Linearization Algorithm
The C3 Linearization Algorithm defines the MRO by combining:
- The class itself.
- The list of parent classes in the order they are specified.
- The MRO of the parent classes in the same order.
Rules of the C3 Linearization Algorithm
-
Preserve local method order: if class
A
is specified before classB
, all methods of classA
should be considered before methods of classB
. -
Maintain order in parent classes: if class
A
is a parent of classB
, then all methods of classA
should be considered before methods of classB
. -
Respect inheritance order: if class
C
is a parent of two or more classes, the order of methods in classC
should be preserved in the MRO of all these classes.
Algorithm Steps:
Step 1. Start with the class itself:
Always start with the class in which the method is called.
Step 2. Add base classes in the order they are listed:
After the current class, check the base classes in the order they are listed during inheritance.
Step 3. Navigate through parent classes:
Look for fields and methods there.
Step 4. Merge parent classes' MRO:
If the same base class is inherited through multiple paths, it is checked only once and in the correct order (all other times it will be skipped).
For those familiar with the "Algorithms and Data Structures" topic, this is a depth-first search, not a breadth-first.
11.2 Checking MRO
In Python, you can check the method and field traversal order of a class
using the __mro__
attribute or the
mro()
function.
Example:
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
def method(self):
print("D")
# Checking MRO
print(D.__mro__)
The output will be:
(<class '__main__.D'>,
<class '__main__.B'>,
<class '__main__.C'>,
<class '__main__.A'>,
<class 'object'>)
This shows the order in which Python will look for methods and attributes:
D
: Python checks the method in classD
first.-
B
: Then Python checks the method in classB
(the first parent class). -
C
: If the method is not found in classB
, Python checks the method in classC
(the second parent class). -
A
: If the method is not found in classesB
andC
, Python checks the method in classA
. -
object
: Finally, Python checks the method in the base classobject
.
11.3 Using super()
with MRO
The super()
function follows the MRO to call methods
of parent classes in the correct order. Let's consider an example of using
super()
:
class A:
def method(self):
print("A")
super().method()
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 = D()
d.method()
The output will be:
D
B
C
A
Traversal Order (MRO)
1. Calling the method
in class D
:
- Python first checks the method in class
D
and finds it there. - Method
D.method()
is executed and prints"D"
. -
Then
super().method()
is called, which follows the MRO to call the next method.
2. Calling the method
in class B
:
- According to the MRO, the next class after
D
isB
. - Method
B.method()
is executed and prints"B"
. -
Then
super().method()
is called, which follows the MRO to call the next method.
3. Calling the method
in class C
:
- The next class in the MRO after
B
isC
. - Method
C.method()
is executed and prints"C"
. -
Then
super().method()
is called, which follows the MRO to call the next method.
4. Calling the method
in class A
:
- The next class in the MRO after
C
isA
. - Method
A.method()
is executed and prints"A"
. -
Then
super().method()
is called, but sinceA
has no parent methodsmethod
(exceptobject
), the call completes with no further actions.
GO TO FULL VERSION