Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Python Multiple Inheritance and Method Resolution Order

Learn how Python combines multiple base classes, resolves inherited attributes with the method resolution order, and uses super() cooperatively across a multiple-inheritance hierarchy.

Levels: Intermediate ~24 min read

Intermediate

Python classes can inherit from more than one base class. Multiple inheritance is not just a list of parents: Python uses a method resolution order (MRO) to decide which class is searched first and how cooperative super() calls continue through the hierarchy.

Define a class with multiple base classes

A class can list two or more base classes. The new class can use behavior supplied by each base, as long as the inherited names can be resolved consistently.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Flyer:
    def fly(self):
        return "flying"

class Swimmer:
    def swim(self):
        return "swimming"

class Duck(Flyer, Swimmer):
    pass

duck = Duck()
print(duck.fly())
print(duck.swim())

Duck combines the two independent behaviors inherited from Flyer and Swimmer.

The order of the bases affects method lookup

When multiple bases provide the same attribute, Python needs a defined search order. The left-to-right base order contributes to that order, so changing the bases can change which inherited implementation is found first.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class EmailSender:
    def send(self):
        return "email"

class FileSender:
    def send(self):
        return "file"

class Notification(EmailSender, FileSender):
    pass

print(Notification().send())

EmailSender appears before FileSender, so its send() implementation is found first for Notification.

Inspect the complete method resolution order

Every class has a __mro__ tuple containing the classes considered during method resolution. Reading it is useful when you need to explain or debug inherited behavior.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class A:
    pass

class B:
    pass

class C(A, B):
    pass

print([cls.__name__ for cls in C.__mro__])

The MRO begins with C and then lists the classes Python searches for inherited attributes.

__bases__ and __mro__ show different views

__bases__ contains only the direct base classes from the class definition. __mro__ contains the complete method-resolution search order, including indirect bases and object when applicable.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class A:
    pass

class B(A):
    pass

class C(B):
    pass

print([base.__name__ for base in C.__bases__])
print([cls.__name__ for cls in C.__mro__])

__bases__ shows only C's direct base B, while __mro__ shows the complete lookup path through C, B, A, and object.

Python uses C3 method resolution order

Python uses the C3 method resolution order. It preserves the local left-to-right ordering of bases, keeps a class from being visited twice in a diamond hierarchy, and produces a consistent linear search path.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Root:
    pass

class Left(Root):
    pass

class Right(Root):
    pass

class Bottom(Left, Right):
    pass

print([cls.__name__ for cls in Bottom.__mro__])

Root is included once in Bottom's MRO even though Bottom reaches it through both Left and Right.

A diamond hierarchy can cooperate through super()

In cooperative multiple inheritance, super() means continue with the next class in the MRO. Each class can perform its own work and then delegate, allowing the whole chain to participate exactly once.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Root:
    def steps(self):
        return ["root"]

class Left(Root):
    def steps(self):
        return ["left"] + super().steps()

class Right(Root):
    def steps(self):
        return ["right"] + super().steps()

class Bottom(Left, Right):
    def steps(self):
        return ["bottom"] + super().steps()

print(Bottom().steps())
print([cls.__name__ for cls in Bottom.__mro__])

The cooperative calls follow Bottom, Left, Right, then Root—the same order shown by Bottom.__mro__.

super() is not simply a call to one named parent

A direct call such as Parent.method(self) names one specific implementation. In a cooperative multiple-inheritance hierarchy, super() is different: it follows the MRO and can allow sibling branches to participate in the same chain.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Root:
    def report(self):
        return ["root"]

class A(Root):
    def report(self):
        return ["A"] + super().report()

class B(Root):
    def report(self):
        return ["B"] + super().report()

class C(A, B):
    def report(self):
        return ["C"] + super().report()

print(C().report())

super() moves through C, A, B, and Root instead of stopping after a single named parent.

Some inheritance combinations cannot produce an MRO

Not every combination of bases has a consistent linear order. When the ordering constraints conflict, Python raises TypeError while creating the class instead of choosing an arbitrary search path.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class X:
    pass

class A(X):
    pass

class B(X):
    pass

class First(A, B):
    pass

class Second(B, A):
    pass

try:
    class Impossible(First, Second):
        pass
except TypeError as exc:
    print(type(exc).__name__)
    

The conflicting requirements from First and Second make a consistent MRO impossible, so class creation raises TypeError.

Use multiple inheritance deliberately

Multiple inheritance is most useful when the participating classes have clearly compatible responsibilities and can cooperate through the MRO. Small, focused mixin-style bases can be useful. When behavior is unrelated or state coordination becomes confusing, composition or simpler inheritance may be clearer.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Printable:
    def label(self):
        return "printable"

class Timestamped:
    def timestamp_label(self):
        return "timestamped"

class Record(Printable, Timestamped):
    pass

record = Record()
print(record.label())
print(record.timestamp_label())

These two focused bases contribute independent behavior without competing over the same method name.

Key takeaways

  • A class can have multiple direct bases, and their ordering contributes to method resolution.
  • __bases__ shows direct bases, while __mro__ shows the complete method-resolution order.
  • Python uses C3 method resolution order to produce a consistent search path and avoid duplicate visits to shared bases.
  • In cooperative multiple inheritance, super() delegates to the next class in the MRO rather than one hard-coded parent.
  • Conflicting inheritance constraints cause TypeError during class creation instead of an ambiguous MRO.
Was this page helpful?

Mark this learning resource complete to track your learning progress.