2.1.10 · D4OOP Fundamentals

Exercises — Multiple inheritance — Python's C3 linearization algorithm

2,008 words9 min readBack to topic
Recall The one rule you need, restated

, base case . Merge: take the head of the first list; it is a good head if it appears in no tail (any position after position 0) of any list. If good, emit it and delete it everywhere; if not, try the head of the next list. If no good head exists → TypeError. Throughout, means object.

Figure — Multiple inheritance — Python's C3 linearization algorithm

Level 1 — Recognition

Exercise 1.1

Given class A: pass, what is ?

Recall Solution

WHAT: Apply the base rule. A has no explicit parents, so its only parent is object. Answer: [A, object].

Exercise 1.2

For class B(A) with class A, is this single or multiple inheritance, and what is ?

Recall Solution

WHAT: One direct parent → Single inheritance. C3 on a single chain just stacks the parent's MRO under the child. Answer: single inheritance; [B, A, object].

Exercise 1.3

In the merge of merge([B, A, O], [C, A, O], [B, C]), is B a good head of the first list?

Recall Solution

WHAT: Check whether B appears in the tail of any list. Tails: [A,O], [A,O], [C]. B is in none of them. Answer: Yes — B is a good head, so it is emitted first.


Level 2 — Application

Exercise 2.1 — the classic diamond

class A, class B(A), class C(A), class D(B, C). Compute by hand.

Recall Solution

Precompute: , .

Iter Lists Try Good? Reason
1 [B,A,O] [C,A,O] [B,C] B not in any tail
2 [A,O] [C,A,O] [C] A A in tail of list 2 → try C
3 [A,O] [A,O] [] A now in no tail
4 [O] [O] [] O

Answer: [D, B, C, A, object].

Exercise 2.2 — asymmetric depth

class A, class B(A), class C(B), class D(A), class E(C, D). Compute .

Recall Solution

Precompute: , , , .

Iter Lists Try Good? Reason
1 [C,B,A,O] [D,A,O] [C,D] C not in any tail
2 [B,A,O] [D,A,O] [D] B not in any tail
3 [A,O] [D,A,O] [D] A A in tail of list 2 → try D
4 [A,O] [A,O] [] A now in no tail
5 [O] [O] [] O

Answer: [E, C, B, A, D, O]... wait — recheck iter 3: after emitting B, D becomes a good head before A because A is still blocked. So the order is E, C, B, D, A, O. Let me re-lay iter 3 cleanly: lists are [A,O] [D,A,O] [D]. Head of list 1 is A, blocked (in tail of list 2). Head of list 2 is D, in no tail → emit D. Lists become [A,O] [A,O] []. Then A, then O. Answer: [E, C, B, D, A, object].

Exercise 2.3 — three parents, flat

class A, class B, class C, class D(A, B, C). Compute .

Recall Solution

Each of A,B,C is [X,O]. A not in any tail → emit. Then B (tail-free) → emit. Then C → emit. Then O. Answer: [D, A, B, C, object] — the written parent order is preserved, and O sinks to the bottom.


Level 3 — Analysis

Exercise 3.1 — spot the inconsistency

class X, class Y, class A(X, Y), class B(Y, X), class C(A, B). Does exist? If not, name the exact contradiction.

Recall Solution

, . Emit A (tail-free). Lists: [X,Y,O] [B,Y,X,O] [B]. Try X: in tail of list 2 → skip. Try B: tail-free → emit. Lists: [X,Y,O] [Y,X,O] []. Try X: in tail of list 2 (Y,X,O) → skip. Try Y: in tail of list 1 (X,Y,O) → skip. No good head. Answer: No valid MRO. Contradiction: A demands X before Y; B demands Y before X. Python raises TypeError: Cannot create a consistent method resolution order (MRO).

Exercise 3.2 — reverse-engineer a super() jump

For D(B, C), B(A), C(A) (MRO [D,B,C,A,O]), a method in B calls super().m(). Which class's m runs next?

Recall Solution

WHAT: super() walks the MRO of the instance's type, not B's literal parent. In L[D], the successor of B is C. Answer: C.m runs — even though B's written parent is A. This is the cooperative behaviour covered in super() and cooperative inheritance.

Exercise 3.3 — monotonicity check

Given L[B]=[B,A,O], is it possible for any subclass of B to have an MRO in which A comes before B?

Recall Solution

WHAT: Monotonicity says relative order in a parent's MRO is never reversed in a subclass. Since B precedes A in , no subclass may put A first. Answer: Impossible — and a child always precedes its parents anyway, so B before A is doubly guaranteed.


Level 4 — Synthesis

Exercise 4.1 — design to a target MRO

You want a class E whose MRO is exactly [E, M1, M2, Base, object], where M1, M2 are Mixins and Base is the concrete class. Write the class headers (parent lists) that produce this, and verify by hand.

Recall Solution

Design: class Base, class M1, class M2, class E(M1, M2, Base). Each mixin/base is [X,O]. Emit M1, then M2, then Base, then O. Answer: class E(M1, M2, Base): ...[E, M1, M2, Base, object]. (List mixins before the concrete base so their overrides win — a standard Mixins pattern.)

Exercise 4.2 — build a valid deep hierarchy

Construct classes so that of the bottom class is [G, E, F, B, C, D, A, object]. One valid design plus its verification.

Recall Solution

Design: A; B(A), C(A), D(A); E(B, C), F(C, D); G(E, F). Precompute: B=[B,A,O], C=[C,A,O], D=[D,A,O], E=[E,B,C,A,O], F=[F,C,D,A,O]. Emit EB → then C blocked (tail of list 2) so try list 2 head F → emit F → now C free → CDAO. Answer: [G, E, F, B, C, D, A, object]. ✅ (Note F jumps ahead of B... let's confirm: after E, lists [B,C,A,O] [F,C,D,A,O] [F]; B tail-free → emit B; lists [C,A,O] [F,C,D,A,O] [F]; C blocked (tail of list 2) → try F, tail-free → emit F; then C, D, A, O. Order emitted: E,B,F,C,D,A. So the real MRO is [G, E, B, F, C, D, A, object].) Corrected answer: the design above yields [G, E, B, F, C, D, A, object]. To get B after F you would need F listed to force it — but C3 keeps B right after E here. Trust the merge, not the guess.


Level 5 — Mastery

Exercise 5.1 — predict, then explain the whole chain

class A, class B(A), class C(A), class D(B, C), class E(D, C). Compute and state whether class E(C, D) (parents swapped) would still be valid.

Recall Solution

, . Emit D (tail-free). Lists: [B,C,A,O] [C,A,O] [C]. B tail-free → emit. Lists: [C,A,O] [C,A,O] [C]. C tail-free (its only tails are [A,O]) → emit. Then A, then O. Answer: [E, D, B, C, A, object]. Swap E(C, D): now the parent list is [C,D]. But already places D before C (as D's own... actually D before C inside L[D]), while E(C,D) demands C before D. Conflict → TypeError. So order matters: E(D, C) works, E(C, D) fails.

Exercise 5.2 — full trace with a print check

For the classes in Ex 5.1, at runtime E().__mro__ should match your hand result. Write the expected tuple exactly.

Recall Solution

Answer: (<class 'E'>, <class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>) — i.e. [E, D, B, C, A, object], confirming Ex 5.1. This is a constrained Topological sort: every child precedes its parents, and no two ancestors' orders were reversed.


Connections

  • Method Resolution Order
  • super() and cooperative inheritance
  • The Diamond Problem
  • Single inheritance
  • Mixins
  • Composition vs Inheritance
  • Topological sort