In Python, if `class D(B, C)` and both B and C override `run()`, which one executes?
answer
- The hierarchy is flattened, not searched
- One linear order per class
- Written base order is preserved
- Shared base comes after both branches
- First class in `__mro__` defining the name wins
basics
~10 sB's run executes. Python flattens the hierarchy into one method resolution order — D, B, C, then their shared base, then object — and calls the first class in that list that defines run.
solid answer
~40 sPython does not search the tree branch by branch; it computes a single linear **method resolution order** (MRO) for D once, at class-creation time, and stores it as the tuple `D.__mro__`. For a diamond where B and C both derive from a common base A, that order is `D, B, C, A, object`, so `D().run()` finds `B.run` first — the leftmost base wins. Two rules produce it: the direct bases keep the order written in the class statement (local precedence), and no class ever appears before one of its own subclasses (monotonicity), which is why the shared base A comes after *both* B and C rather than straight after B. The lookup itself is a plain left-to-right scan of that tuple for the first class whose own namespace holds the name.
code
python · 14 linesclass Base:
def route(self): return "Base"
class Cached(Base):
def route(self): return "Cached"
class Logged(Base):
def route(self): return "Logged"
class Router(Cached, Logged):
pass
print(Router().route())
print([c.__name__ for c in Router.__mro__])go deeper
Be ready to state the order out loud for a four-class diamond and say that the leftmost base wins. Knowing that Cls.__mro__ exists and that you can print it is enough to pass this at your level.
Explain the mechanics: one linear order computed at class creation, scanned left to right for the first class defining the name, with the shared base placed after both branches rather than after the leftmost one.
Show you use the MRO as a debugging tool — printing it before reasoning about which implementation a hierarchy will pick — and that you can explain why a depth-first mental model produces the wrong answer in real code.
Own the design consequence: a linear, total order is what makes multiple inheritance analysable at all, and deep hierarchies whose behaviour only makes sense after printing the order are a signal to prefer composition.
### The mechanism When a class statement executes, CPython computes one flat, ordered sequence of that class and all of its ancestors — the **method resolution order**. It is stored on the class as the tuple `Cls.__mro__` and is computed exactly once, at class creation. Every attribute lookup that has to consult the class hierarchy — `D().run`, `D.run`, an implicit dunder lookup — walks that single tuple from left to right and stops at the first class whose own namespace (`__dict__`) contains the name. There is no tree walk at lookup time, and no backtracking. So the diamond question is really "what does the MRO look like", and the answer for ```python class A: ... class B(A): ... class C(A): ... class D(B, C): ... ``` is `D, B, C, A, object`. `B.run` is found before `C.run`, so `D().run()` returns B's implementation. ### Why that order and not `D, B, A, C, object` Two constraints shape it. **Local precedence order**: the direct bases appear in the MRO in the order you wrote them in the class statement, so B precedes C. **Monotonicity**: a class never appears before any of its subclasses, so the shared base A has to come after both B and C, not after B alone. Together they push all the branches ahead of the ancestor they share. A naive depth-first walk would produce `D, B, A, C` and would therefore find `A.run` before ever reaching `C` — meaning C's override of a method it inherited from A would be silently invisible to D. Python's algorithm (C3 linearization) exists precisely so that cannot happen. ### Reading it in practice ```python class Base: def route(self): return "Base" class Cached(Base): def route(self): return "Cached" class Logged(Base): def route(self): return "Logged" class Router(Cached, Logged): pass Router().route() # 'Cached' [c.__name__ for c in Router.__mro__] # ['Router', 'Cached', 'Logged', 'Base', 'object'] ``` When you are unsure what a hierarchy will do, print the MRO rather than reasoning about the diagram. `Cls.__mro__` gives the tuple; `Cls.mro()` returns the same sequence as a fresh list; `inspect.getmro(Cls)` is the introspection-friendly spelling. Reading the list top to bottom *is* reading the lookup order. ### What the rule does and does not cover * It is per **name**, not per class. If `run` is defined on B and `close` only on C, `D` gets B's `run` and C's `close`. The MRO is one order, but each attribute is resolved independently against it. * The order is fixed; the *contents* are not. Assigning `C.run = something` after the fact is picked up immediately, because lookup consults each class's namespace live — it is only the sequence of classes that is precomputed. * Instance state comes first for ordinary attributes: an instance's own `__dict__` entry shadows anything found through the MRO. The MRO answers "which class supplies this method", not "is there an instance attribute of the same name". * An implicit dunder invocation (`len(d)`, `d + x`) is looked up on the *type*, so it uses D's MRO and skips the instance dictionary entirely. ### The interview trap Candidates who answer "the leftmost base wins" are right about this example but often extend it wrongly to "depth-first from the left", which gives the wrong answer as soon as the second branch overrides something from the shared base. The safe formulation is: *every class comes before its own ancestors, and among unrelated classes the written base order is preserved.* If both constraints cannot be satisfied at once, Python does not guess — it refuses to create the class with a `TypeError`. One more practical consequence: because the order is linear and total, a call chain that hands off to the next class in line is well defined for the whole hierarchy, not just for the class you are standing in. That is what makes cooperative multiple inheritance workable in Python at all, and it is why the answer to "whose method runs" is always determined by D's own MRO — never by B's or C's view of the world in isolation. ### The same order answers more than "whose method" The linearization is not only the method-lookup path. `issubclass(D, A)` is true precisely because A appears in D's order, and an `except` clause matching an exception class is decided the same way. Class-level data follows it too: a class attribute defined on B and again on C is read from B for instances of D, which is a common source of surprise when a configuration constant is set on the branch that happens to sit second. And because the order is a property of the object's *actual* class, the same inherited method body can resolve a name differently depending on which subclass created the instance — the deciding order is always the one belonging to the instance in hand, never the one belonging to the class the code was written in. When a hierarchy behaves unexpectedly, the first move is therefore to print `type(obj).__mro__`, not to reread the method bodies.
- If C defines `close()` and B does not, does `D()` still get C's `close`?Yes. The MRO is one order, but each name is resolved separately against it. Looking up `close` walks `D, B, C, ...` and stops at C, the first class whose namespace holds that name. The leftmost base only wins for names it actually defines; it does not shadow the rest of the order wholesale.
- Where does an instance attribute fit relative to the MRO?For an ordinary attribute, the instance's own `__dict__` is consulted before the class chain, so `d.run = f` shadows `B.run` for that instance. Implicit dunder invocations are the exception: they are looked up on the type, so they follow D's MRO and ignore the instance dictionary entirely.
- Is the MRO recomputed if you add a method to C after D exists?No. The sequence of classes is computed once when the class statement runs and cached on the class, but each class's namespace is read live at lookup time. So `C.run = something` takes effect immediately for D, while the *order* in which D consults its ancestors never changes.
It is a queue at a service desk, not a family tree: Python lines every ancestor up once, in a fixed order, and asks each in turn until one can answer.
saying these in an interview costs you the question
- Says Python searches depth-first up the leftmost branch
- Claims the shared base is reached before the second base
- Thinks the diamond raises an ambiguity error at call time
- Believes the order is recomputed on every attribute access
- Says the rightmost base wins because it is 'more specific'