Python’s Method Resolution Order (MRO) is the algorithm used to determine the order in which to search for a method in a class hierarchy, especially when dealing with multiple inheritance.

Let’s see MRO in action with a concrete example. Imagine we have a Base class, and then two classes, Left and Right, that inherit from Base. Finally, we have a Child class that inherits from both Left and Right.

class Base:
    def greet(self):
        print("Hello from Base!")

class Left(Base):
    def greet(self):
        print("Hello from Left!")

class Right(Base):
    def greet(self):
        print("Hello from Right!")

class Child(Left, Right):
    pass

c = Child()
c.greet()

When c.greet() is called, Python doesn’t just pick the first greet it finds. It consults the MRO for Child to figure out the exact sequence of classes to check. In this case, the output is:

Hello from Left!

This tells us Python looked at Left first, found greet there, and executed it, never even reaching Right or Base.

The problem MRO solves is the "diamond problem" in multiple inheritance. Without a defined order, if two parent classes have a method with the same name, Python wouldn’t know which one to call. MRO provides a deterministic and consistent way to resolve these ambiguities. It ensures that each class is visited only once and that the linearization of the inheritance graph is maintained.

Internally, Python uses the C3 linearization algorithm to compute the MRO. This algorithm guarantees that if a class A inherits from B and C, then A must appear before B and C in the MRO list, and the MRO of B and C themselves must be preserved. The MRO is computed at class creation time and is stored in a special attribute __mro__ on the class object. You can inspect it directly:

print(Child.__mro__)

This would output something like:

(<class '__main__.Child'>, <class '__main__.Left'>, <class '__main__.Right'>, <class '__main__.Base'>, <class 'object'>)

Notice how Child is first, followed by Left, then Right, then Base, and finally the ultimate base class object. This is the exact order Python searches for methods.

The order of base classes in the class definition (class Child(Left, Right):) directly influences the MRO. If we defined class Child(Right, Left):, the MRO would change:

class Child(Right, Left):
    pass

c = Child()
print(Child.__mro__)
c.greet()

Output:

(<class '__main__.Child'>, <class '__main__.Right'>, <class '__main__.Left'>, <class '__main__.Base'>, <class 'object'>)
Hello from Right!

This demonstrates that the order of inheritance matters significantly and is the primary lever you control for determining method resolution.

A common pitfall is assuming that Python’s MRO simply does a depth-first or breadth-first search. The C3 algorithm is more sophisticated, ensuring that local precedence order is maintained. For instance, if Child inherited from A and B, and A inherited from C and D, and B also inherited from C and D, the MRO would prioritize A over B, and then preserve the MRO of A and B (which would involve C and D in a specific order). The rule is: a subclass must appear before its superclasses, and if a class X appears in the MRO of two different base classes, it must appear in the MRO of the derived class at the same position relative to those two base classes.

When you encounter a TypeError: ... takes 0 positional arguments but 1 was given in a multiple inheritance scenario, it’s almost always because the super() call within a method isn’t resolving to the expected parent class due to an MRO mismatch or an incorrect super() signature. You need to ensure your super() calls align with the MRO computed for your class.

The next concept you’ll likely grapple with is how super() interacts with MRO, especially in complex hierarchies and when dealing with class initialization (__init__).

Want structured learning?

Take the full Python course →