Why is a mixin listed before the concrete base class in a Python class statement?
answer
- Left to right decides who wins
- The mixin must sit in front
- Wrong order raises nothing at all
- super() continues rightwards to the base
- Check Cls.__mro__ and the method's __qualname__
basics
~20 sAttribute lookup walks the class's method resolution order left to right, so a mixin listed after the concrete base is shadowed by it. Putting the mixin first lets its method win, and its super() call still reaches the base.
solid answer
~40 sBases are searched left to right, so the leftmost class that defines a name supplies it. A mixin exists to override or wrap something the concrete base provides, which only works if the mixin comes first: `class Right(UpperMixin, Report)` runs `UpperMixin.render`, while `class Wrong(Report, UpperMixin)` runs `Report.render` and the mixin contributes nothing. The mixin's `super().render()` then continues rightwards along the same order and lands on the base, so the base's behaviour is wrapped rather than discarded. The failure mode is what makes this an interview question: the wrong order raises nothing at all, it just silently reverts behaviour. Check the actual order with `Cls.__mro__`, and remember that among several mixins the leftmost wraps the ones to its right.
code
python · 21 linesclass Report:
def render(self):
return "panel"
class UpperMixin:
def render(self):
return super().render().upper()
class Right(UpperMixin, Report):
pass
class Wrong(Report, UpperMixin):
pass
print(Right().render())
print(Wrong().render())
print([c.__name__ for c in Wrong.__mro__])go deeper
Remember the convention and the reason: bases are searched left to right, so a mixin placed after the concrete base never gets a chance to run. Know how to print a class's resolution order.
Be ready to trace both orders on a small example and say exactly what each prints, and to explain that a mixin's super() call continues to the next class in the order rather than to a fixed base class.
Show the production angle: this fails silently, so you defend it with behaviour-level tests rather than with the convention, and you treat any reordering of a bases tuple in review as a behavioural change.
Own the policy question of how much layering via base order a codebase should carry at all, given that the ordering is invisible at the call site and survives only as long as the tests that pin it.
**The rule in one line: bases are searched left to right, so the leftmost definition wins.** Looking up `obj.render()` walks the class's method resolution order and stops at the first class that defines `render`. A mixin is written to override or decorate something the concrete base already provides, so it must appear earlier in that order than the base. In practice that means the convention *mixins first, concrete base last* is not style, it is the difference between the mixin working and doing nothing. ## What the wrong order looks like Given `class UpperMixin` whose `render` upper-cases the result of `super().render()`, and a concrete `Report` whose `render` returns a string: - `class Right(UpperMixin, Report)` produces the upper-cased output. - Flip the bases to `class Wrong(Report, UpperMixin)` and `Report.render` is found first, returns its string, and never delegates onwards. The mixin is still in the hierarchy, still shows up in `Wrong.__mro__`, still passes an `isinstance` check, and never runs. Nothing raises, no warning appears; the only visible symptom is that a feature quietly went missing. ## Why the mixin still reaches the base A mixin that wants to wrap rather than replace calls `super()`, which does not mean *my base class* but *the next class after me in this instance's resolution order*. With `Right`, the order is `Right`, `UpperMixin`, `Report`, `object`, so `UpperMixin.render`'s `super().render()` resolves to `Report.render`. This is why a mixin can be written without knowing which concrete class it will end up in front of, and why it must be positioned in front of that class for the delegation to have anywhere useful to go. ## Order among several mixins With `class C(A_Mixin, B_Mixin, Base)`, `A_Mixin` wraps `B_Mixin`, which wraps `Base` — a layered pipeline where the leftmost is outermost. - If the mixins are truly independent, order between them is irrelevant; - if their effects compose (one normalises, one logs, one caches), the order encodes real behaviour and deserves a comment and a test, because a later reader alphabetising or tidying the bases tuple will otherwise reorder your pipeline without knowing it. ## When a mixin deliberately does not call super() Omitting the `super()` call turns the mixin from a wrapper into a replacement: the base's implementation never runs. That is legitimate when the mixin is meant to substitute the behaviour outright, but it must be deliberate, because every class further right in the order is cut off too. The reverse mistake is just as common: a mixin correctly placed first but silently ending the chain, so callers wonder why the base's work stopped happening. ## Verifying rather than guessing Two one-liners settle almost every question. - `[c.__name__ for c in Cls.__mro__]` prints the exact search order, - and `Cls.render.__qualname__` names the class that actually supplies the method — `Report.render` versus `UpperMixin.render` is the whole diagnosis. Reach for those before theorising about the ordering rules. ## Where ordering becomes an error rather than a silent no-op Some base orders cannot be linearised at all and the `class` statement raises a `TypeError` when the class is created — that happens when the requested order contradicts an order already fixed by one of the bases. The detailed linearisation rules are their own subject; the practical takeaway here is the asymmetry: - an *impossible* order fails loudly at import time, - while a merely *wrong* order is accepted happily and only shows up as missing behaviour. ## Derive the mixin from object, not from the concrete base A mixin that inherits from the class it is meant to decorate stops being composable: - it now drags that base along wherever it is used, - it constrains the orders that can be linearised at all, - and it can no longer be applied to a sibling class in the same family. Keep the mixin's own base list empty so it sits directly under `object`, and let the class statement that combines them be the only place the pairing is decided. If the mixin genuinely cannot work without a specific base's attributes, that is a contract to declare with an abstract method rather than to enforce by inheriting. ## One more reason to keep mixins stateless If mixins carry no state, base order affects only which method is found, which is the concern above and is testable. Once mixins carry state and initialisation, order also decides who initialises what and in which sequence, and the same reordering that used to cost you an override now costs you a half-constructed object. Statelessness is what keeps *mixins first* a simple, reliable rule rather than a fragile one.
- What happens if the mixin is listed first but never calls super()?It replaces the base's implementation instead of wrapping it: everything further right in the resolution order is cut off for that method. That is fine when substitution is the intent, but it is a common accident — the mixin appears to be positioned correctly, yet the base's work silently stops happening. If the mixin is meant to decorate, the `super()` call is the whole mechanism.
- Two mixins in front of a base: does the order between them matter?It matters whenever their effects compose. The leftmost is outermost and wraps the one to its right, so a normalising mixin ahead of a logging mixin logs normalised values and the reverse logs raw ones. If the two are genuinely independent, the order is irrelevant — but say so in a comment, because otherwise the next reader cannot tell which case they are in.
- How do you check which class actually supplies a method at runtime?Print `[c.__name__ for c in Cls.__mro__]` for the search order, and `Cls.method.__qualname__` for the class that owns the implementation being found. Together they answer the question in seconds and settle it with evidence rather than by reasoning about the bases tuple.
saying these in an interview costs you the question
- Thinks the rightmost base wins attribute lookup
- Says base order is only cosmetic or stylistic
- Expects the wrong order to raise instead of silently doing nothing
- Believes super() always means the concrete base class
- Puts the concrete base first out of habit
- Reorders a bases tuple while tidying without running the tests