What is MRO in python ?

Yokesh Chowdary
5 min readJun 16, 2022

--

MRO (Method Resolution Order)is the algorithm used by python to call the appropriate methods in the specific order when the object of the derived class calls a method with 2 or more base classes.

Multiple Inheritance uses MRO technique to call the methods. Without MRO duplicates will be populated in the code.

MRO follows Depth-First-Left-Right order for calling methods of a object whose class inherits from more than 1 class. Let’s see in detail :

Look at this code below : Example 1

class X:
pass


class Y:
pass


class Z:
pass


class A(X,Y):
pass


class B(Y,Z):
pass


class M(B,A,Z):
pass


print(M.__mro__)

output : (<class ‘__main__.M’>, <class ‘__main__.B’>, <class ‘__main__.A’>, <class ‘__main__.X’>, <class ‘__main__.Y’>, <class ‘__main__.Z’>, <class ‘object’>)

Explanation : Let’s assume i have an object ‘m’ for class ‘M’ . If i call m.say_hello() , python follows the series of steps as mentioned below.

step1 : python looks for say_hello() in class ‘M’ , if method not found , python goes to left most base class in M(B,A,Z) i.e, it goes to class ‘B’.If method found in class ‘M’ searching stops immediately.

step2 : In class ‘B’ python checks for say_hello(), if say_hello() is not found, it again goes to left most base class of B(Y,Z) i.e, class Y .

step3 : In class ‘Y’ python checks for say_hello() if it is not found it goes to the base class of ‘Y’ i.e, object class (All user defined classes derived from object base class).If say_hello() is not found in object class too, python goes back to class ‘Y’ origin i.e, class B(Y,Z) .

step4: Now, python checks in class ‘Z’ if method is not found , python checks in class ‘Z’ base class i.e, class object, If method is not there in class object python goes back to class M(B,A,Z) .

step5: As we already went through all base classes of ‘B’, Now python checks in all base classes of next left most base class i.e, class ‘A’.

step6: This process continues until python finds the method ‘say_hello()’

step7 : If this method is not found in any of the base classes, it throws error finally

“Feeling a bit tricky and confusing right ?”

I got a shortcut to explain the order of mro search . Write the classes in order using Depth-First-Left-Right process.I will explain how to write mro manually using D-F-L-R. Have a look below …

Analyzing MRO manually : Example 2

class A:
pass
class B:
pass
class C(A, B):
pass
print(C.__mro__)
# output :

Depth to First means , Start with the class where the object of that class calls the method. Let’s assume i called C().some_method(). So, Depth class means class ‘C’ . Now start with Depth and moves Left , we can see Class ‘A’ base class so write like this C -> A . Now, navigate to class ‘A’ that means we are navigating as Depth to First , We all know all user defined classes have object as base class so chain will be C -> A -> object. For object there is no base class so come back to entry point i.,e class C(A,B). We already went through all base classes of ‘A’ . So we need to go Left-Right i.e, to class ‘B’ . Now, chain will be C->A->object->B. Navigate to class ‘B’ , class ‘B’ has a base class ‘object’. Now chain will be C->A->object->B->object.This is the pre-mro of class ‘C’. pre-mro has duplicates right ? (2 objects),We need to remove those duplicates to get the final mro called post-mro. Remove duplicates starting from right of the chain and move towards left of the chain. Now, post-mro of class ‘C’ becomes C->A->B->object.

pre-mro : C->A->object->B->object

post-mro : C->A->B->object. Compare this post-mro with the print(C.__mro__) in Example2,Both are same right? That’s how python internally calculates mro for a given sub-class.

Calculating mro manually for Example1

Pre-mro-order : M -> B -> Y -> Object -> Z -> Object -> A -> X -> Object -> Y -> Object -> Z -> Object

Look at the order above, you can see many duplicates right? Just remove all duplicates starting from righthand side and move towards left.

Post-mro-order : M -> B -> A -> X -> Y -> Z -> Object

Now, Compare the ‘Post-mro-order’ with the output we got using print(‘M.__mro__’) from the last line of the code.

Both are same right ?

This is the process python uses mro to search for the particular method .

“Let’s see some examples to understand deeply”

class A:

def method(self):
print("A.method() called")


class B:

def method(self):
print("B.method() called")


class C(A, B):
pass


class D(C, B):
pass


d = D()
d.method()

Guess the output ?

output : A.method() called

Let’s have a look at mro calculation manually

pre-mro for class ‘D’ is : -> C -> A -> Object -> B -> Object -> B -> Object

post-mro-after-removing-duplicates : D -> C -> A -> B -> Object

output explanation using post-mro-after-removing-duplicates : python looks for method() in class ‘D’ , But there is no method() in class ‘D’. So, python looks in left most base class of class ‘D’.In class ‘C’ it cannot find method() . Now, it goes to class ‘A’ there it finds method() and it calls that method and “A.method() called” is printed.Now python stops searching for the method().

“Now, Try to run mro on class D”

print(D.__mro__) gives : (<class ‘__main__.D’>, <class ‘__main__.C’>, <class ‘__main__.A’>, <class ‘__main__.B’>, <class ‘object’>)

The above output is same as the post-mro-after-removing-duplicates

“Another Example to get more about MRO”

class First(object):

def __init__(self):

print("First(): entering")
super(First, self).__init__()
print("First(): exiting")


class Second(object):

def __init__(self):

print("Second(): entering")
super(Second, self).__init__()
print("Second(): exiting")


class Third(First, Second):

def __init__(self):

print("Third(): entering")
super(Third, self).__init__()
print("Third(): exiting")


x = Third()
print(Third.__mro__)
# output : (<class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class 'object'>)

Let’s calculate mro manually

pre-mro : Third → First → object → Second → object

post-mro(after removing duplicates) : Third → First → Second → object

Code Explanation using post-mro(after removing duplicates):

According to MRO Third.__init__ executes.
prints Third(): entering
then super(Third, self).__init__() executes and MRO returns First.__init__ which is called.
First.__init__ executes.
prints First(): entering
then super(First, self).__init__() executes and MRO returns Second.__init__ which is called.
Second.__init__ executes.
prints Second(): entering
then super(Second, self).__init__() executes and MRO returns object.__init__ which is called.
object.__init__ executes (no print statements in the code there)
execution goes back to Second.__init__ which then prints Second(): exiting
execution goes back to First.__init__ which then prints First(): exiting
execution goes back to Third.__init__ which then prints Third(): exiting
This details out why instantiating Third() results in to :

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting
The MRO algorithm has been improved from Python 2.3 onwards to work well in complex cases,
but I guess that using the "depth-first left-to-right traversal" + "removing duplicates expect for the last"
still works in most cases (please comment if this is not the case).
Be sure to read the blog post by Guido!
LINK : http://python-history.blogspot.nl/2010/06/method-resolution-order.html
'''

I Hope this blog gives you good understanding on MRO. Feel free to comment for more information and doubts.

Thank YOU.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

--

--