I’ve often run into scenarios where a class inherits from multiple parents. It can get confusing quickly when those parent classes have methods with the same name.
Python uses a specific set of rules called Method Resolution Order (MRO) to decide which method to run first.
Understanding MRO is essential if you want to master Object-Oriented Programming (OOP) and avoid hard-to-trace bugs in your code.
In this tutorial, I will show you exactly how Python searches for methods and how you can control this behavior in your own projects.
What Exactly is Method Resolution Order (MRO)?
Method Resolution Order is the order in which Python looks for a method or an attribute in a class hierarchy.
When you call a method on an object, Python starts looking in the current class.
If it doesn’t find it there, it moves to the parent classes based on a specific algorithm.
In older versions of Python, this was simpler, but modern Python uses something called C3 Linearization.
I find it helpful to think of it as a roadmap that Python follows to ensure it doesn’t visit the same class twice or skip a parent.
Why Should You Care About MRO?
If you are building a simple application, you might never need to worry about MRO.
However, as soon as you start using multiple inheritance, things get interesting.
Without a clear order, Python wouldn’t know which “Save” or “Calculate” method to use if three different parents define it.
Mastering this concept allows you to build more flexible and reusable code, especially when working with large frameworks.
The C3 Linearization Algorithm
Python 3 uses the C3 Linearization algorithm to determine the MRO.
It ensures that a child class always appears before its parents and that the order of parents is preserved.
It also solves the “Diamond Problem,” which occurs when two subclasses inherit from the same base class.
Method 1: Use the __mro__ Attribute
One of the easiest ways I check the MRO of a class is by using the built-in __mro__ attribute. This attribute returns a tuple of classes in the order Python will search them.
Let’s look at a practical example involving a US-based logistics system.
# Logistics System Example
class Transportation:
pass
class AirFreight(Transportation):
pass
class GroundShipping(Transportation):
pass
class FedEx(AirFreight, GroundShipping):
pass
# Checking the MRO
print(FedEx.__mro__)You can see the output in the screenshot below.

When you run this code, Python will show you the sequence: FedEx, AirFreight, GroundShipping, Transportation, and finally object.
I use this constantly when I’m debugging a new library to see exactly where a method is coming from.
Method 2: Use the mro() Method
Another way to see the search order is by calling the mro() method on the class itself.
This returns a list instead of a tuple, which can be easier to iterate through if you are automating documentation.
I prefer this method when I need to programmatically verify that my class hierarchy is structured correctly.
# Real Estate Management Example
class Building:
def description(self):
return "Generic Building in New York"
class Residential(Building):
def description(self):
return "Residential Apartment in Manhattan"
class Commercial(Building):
def description(self):
return "Commercial Office in Chicago"
class MixedUse(Residential, Commercial):
pass
# Displaying the MRO list
print(MixedUse.mro())You can see the output in the screenshot below.

In this case, MixedUse will prioritize Residential over Commercial because Residential was listed first in the inheritance brackets.
The Role of the super() Function
The super() function is closely tied to MRO, and it is often misunderstood. Most developers think super() just calls the parent class, but it actually calls the next class in the MRO.
This is a subtle but massive distinction in multiple inheritance.
Let’s look at an example involving a US Payroll system where we calculate bonuses for different roles.
class Employee:
def __init__(self, name):
self.name = name
print(f"Initializing Employee: {self.name}")
class Manager(Employee):
def __init__(self, name):
print("Entering Manager init")
super().__init__(name)
print("Exiting Manager init")
class Developer(Employee):
def __init__(self, name):
print("Entering Developer init")
super().__init__(name)
print("Exiting Developer init")
class TeamLead(Manager, Developer):
def __init__(self, name):
print("Entering TeamLead init")
super().__init__(name)
print("Exiting TeamLead init")
# Creating a TeamLead instance
lead = TeamLead("Alice")You can see the output in the screenshot below.

If you watch the output, you’ll see that super() in the Manager class actually calls the Developer class next.
This happens because Developer follows Manager in the TeamLead MRO.
Solve the Diamond Problem in Python
The Diamond Problem is a classic OOP headache where a class inherits from two classes that both inherit from a single base class.
In some languages, this causes ambiguity or duplicate calls to the base class.
Python’s MRO handles this gracefully by ensuring the base class is only called once at the very end.
Imagine a US Banking app where we have different types of accounts.
class BankAccount:
def __init__(self):
print("Base BankAccount initialized")
class SavingsAccount(BankAccount):
def __init__(self):
print("SavingsAccount initialized")
super().__init__()
class CheckingAccount(BankAccount):
def __init__(self):
print("CheckingAccount initialized")
super().__init__()
class PremiumAccount(SavingsAccount, CheckingAccount):
def __init__(self):
print("PremiumAccount (Diamond) initialized")
super().__init__()
# Instantiate the Premium Account
account = PremiumAccount()Python will search PremiumAccount -> SavingsAccount -> CheckingAccount -> BankAccount.
This linear order prevents the BankAccount from being initialized twice, which could corrupt data in a real-world app.
How Local Precedence Affects MRO
Local precedence means that the order in which you list parent classes in the definition matters.
If I define class MyClass(A, B):, Python will always look at A before B.
Changing that order to class MyClass(B, A): completely flips the method resolution for those two branches.
I’ve seen many developers struggle with bugs simply because they swapped the order of mixin classes in their definitions.
Always put the most specific or “important” class first if there are overlapping methods.
Handle Inconsistent MRO Errors
Sometimes, Python will throw a TypeError: Cannot create a consistent method resolution order (MRO).
This happens when you try to create a hierarchy that violates the rules of C3 Linearization.
For example, if you try to inherit from a parent and then a child of that same parent in a weird order.
class A: pass
class B(A): pass
# This will raise an error because A cannot come before B in some paths and after B in others
# class C(A, B): pass If you see this error, it usually means your class architecture is a bit too “tangled.” I usually solve this by drawing out the hierarchy on paper to see where the circular logic is happening.
Practical Example: A US Healthcare System
Let’s put everything together with a more complex example.
Suppose we are managing a healthcare platform with different types of providers.
class HealthcareProvider:
def get_info(self):
return "General Healthcare Provider"
class Specialist(HealthcareProvider):
def get_info(self):
return "Medical Specialist"
class Telehealth(HealthcareProvider):
def get_info(self):
return "Telehealth Virtual Service"
class CardiologyTelehealth(Specialist, Telehealth):
def get_info(self):
# Using super() to fetch the next method in MRO
parent_info = super().get_info()
return f"Cardiology {parent_info} based in the USA"
# Implementation
service = CardiologyTelehealth()
print(service.get_info())
print("MRO:", [c.__name__ for c in CardiologyTelehealth.mro()])In this example, CardiologyTelehealth will first look at Specialist. If Specialist didn’t have get_info, it would then look at Telehealth.
This structure allows us to layer functionality without repeating code across different departments.
Best Practices for Managing MRO
Keep your inheritance hierarchies as flat as possible to avoid confusion.
Deeply nested multiple inheritance makes it very hard for other developers to follow the logic.
Use super() consistently instead of calling parent classes by name (e.g., Parent.method(self)).
Explicitly calling a parent by name can bypass the MRO and lead to the base class being called multiple times.
Always check the __mro__ if you are unsure about which version of a method is being executed.
Summary of Python MRO Rules
- Python looks for methods in the current class first.
- It then follows the order of classes listed in the inheritance tuple (left to right).
- A child’s class is always checked before its parents.
- If multiple parents inherit from the same base class, that base class is checked last (C3 Linearization).
- You can view this order using Class.__mro__ or Class.mro().
I hope you found this tutorial on Python’s Method Resolution Order helpful!
Understanding MRO was a “lightbulb moment” for me when I first started building large-scale applications.
It takes away the mystery of why certain methods are called and gives you full control over your object-oriented designs.
You may read:
- How to Use isinstance() and issubclass() Functions
- How to Use Python __dict__ Attribute
- Difference Between dir() and vars() in Python
- Python Memory Management

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.