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. Because every class's super() call resolves against this single shared MRO (not against its own direct parent), 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 (e.g., one class says B before C, another says C before B, and something inherits from both), C3 has no valid linearization and Python raises TypeError: Cannot create a consistent method resolution order at class definition time, rather than silently picking an arbitrary order.

Interview-ready summary: 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.