What is the diamond problem in multiple inheritance, and how does Python resolve it?

6 minadvancedoopdiamond-problemmromultiple-inheritance

Quick Answer

The diamond problem occurs when a class inherits from two classes that share a common ancestor (D(B, C) where both B and C inherit from A). Naive resolution could visit A twice or pick an ambiguous method. Python resolves it with C3 linearization, which guarantees each ancestor appears exactly once in the MRO, in a consistent order, so super() calls correctly chain through B and C before reaching A a single time.

Detailed Answer

The shape of the problem

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

class B(A):
    def method(self):
        print("B.method")
        super().method()

class C(A):
    def method(self):
        print("C.method")
        super().method()

class D(B, C):
    def method(self):
        print("D.method")
        super().method()

D().method()
# D.method
# B.method
# C.method
# A.method

Both B and C inherit from A, so D, which inherits from both, forms a "diamond" (D → B → A, D → C → A). A naive depth-first search would call A.method after B (since B's parent is A), then call A.method again after C — visiting A twice and giving C no chance to run before A does.

How C3 linearization fixes it

D.__mro__ is [D, B, C, A, object]A appears exactly once, after both B and C. Every class's super() call resolves against this single shared MRO, not against its own direct parent. So the chain D → B → C → A runs each class's method exactly once, in a well-defined order that respects D's declared base order (B before C).

Why this matters practically: cooperative mixins

This is exactly the mechanism that makes mixin composition safe:

class Base:
    def __init__(self):
        print("Base init")

class Mixin1(Base):
    def __init__(self):
        print("Mixin1 init")
        super().__init__()

class Mixin2(Base):
    def __init__(self):
        print("Mixin2 init")
        super().__init__()

class Combined(Mixin1, Mixin2):
    pass

Combined()
# Mixin1 init
# Mixin2 init
# Base init

Base.__init__ runs exactly once, after both mixins, not once per mixin and not skipped. Without C3's guarantee of a single consistent linearization, composing mixins that share a common base would be far more error-prone.

When Python can't resolve it

If the declared base-class orders are mutually contradictory (one class says B before C, another says C before B, and something inherits from both), C3 has no valid linearization. Python raises TypeError: Cannot create a consistent method resolution order at class definition time, rather than silently picking an arbitrary order.

The diamond problem is the ambiguity of which shared ancestor's method runs, and in what order, when two parent classes share a common base. Python's C3 linearization algorithm computes a single MRO where every ancestor appears exactly once in a consistent order, which is what makes super()-based cooperative multiple inheritance well-defined instead of ambiguous.