2.1.9 · D2OOP Fundamentals

Visual walkthrough — `super()` — calling parent methods

1,660 words8 min readBack to topic

We start from absolute zero. Every word — class, method, override, MRO, proxy — is built before it is used.


Step 1 — What a "class" and a "method" even are

WHAT. Picture three boxes stacked in a family: Animal at the bottom (the grandparent), Dog sitting on top of it (the child). An arrow points from child up to parent — that arrow is called inheritance (see Inheritance — extending classes).

WHY. super() only makes sense once there is an above and a below. The arrow is the road super() will travel.

PICTURE. Two boxes, one arrow. The child box carries its own __init__; the parent box carries its __init__. Same name, two versions — that name-clash is the whole reason super() exists.

Figure — `super()` — calling parent methods

Step 2 — What "override" means, and the trap it sets

WHAT. Both boxes have a method named __init__. When you build a Dog, Python looks in Dog first, finds __init__ there, runs it, and stops looking.

WHY show this? Because "stops looking" is the danger. The parent's __init__ set self.legs = 4. If the child overrides and never reaches up, legs is never created — an `AttributeError` waiting to happen.

PICTURE. A search-beam enters Dog, hits __init__, and halts. The parent's __init__ sits in the dark, un-run. The red "STOP" is the bug super() fixes. (See also Method Overriding vs Overloading.)

Figure — `super()` — calling parent methods

Step 3 — The MRO is just a list, laid out flat

WHAT. For our simple pair:

  • Dog — the class of the object we made; search begins here.
  • Animal — the parent; searched only if Dog lacks the name.
  • object — the invisible ancestor every Python class ends at; it has a do-nothing __init__.

WHY a flat line and not a tree? Because a tree has forks, and a fork forces a choice ("which parent first?"). A line has no forks — the choice is already baked in. super() never decides; it just reads the next slot.

PICTURE. Three beads on a wire, an arrow pointing right labelled "search direction." This wire is the track our marble rolls along in every remaining step.

Figure — `super()` — calling parent methods

Step 4 — super() is a cursor pointing at "the next bead"

WHAT. The equivalence the parent note stated:

  • Dog — the class the code is written in (Python fills this in for you).
  • self — the live object; the proxy keeps hold of it so you never pass it again.
  • The result: "find the bead after Dog on type(self).__mro__."

WHY no self in super().__init__(name)? The proxy already clutches self. Hand it self a second time and Python complains: got multiple values for argument self. One marble, one rider.

PICTURE. A cursor sitting between Dog and Animal, arrow pointing at Animal. A small tag "self bound" rides on the cursor.

Figure — `super()` — calling parent methods

Step 5 — Watch one call: Dog("Rex") step by step

WHAT. Trace Worked Example 1 as motion along the wire:

  1. Dog.__init__ runs. It says super().__init__(name) → cursor jumps to Animal.
  2. Animal.__init__ runs: sets self.name = "Rex", self.legs = 4.
  3. Control returns to Dog.__init__, which now adds self.breed.

WHY parent first, then child? So the foundation (name, legs) exists before the child stacks its own floor (breed) on top. Extend, don't replace.

PICTURE. A marble rolls Dog → Animal, then returns; the state dictionary fills bottom-up: first name + legs appear (green), then breed (yellow).

Figure — `super()` — calling parent methods

Step 6 — The diamond: why a flat line beats the naive call

WHAT. C3 linearization (Method Resolution Order (MRO) & C3 Linearization) collapses the ◇ into one line:

  • A appears once, at the far end — after both B and C.

WHY does super() inside B go to C, not A? This is the surprise. super() never means "my parent." It means "the next bead on this line." In D's line, the bead after B is C, not A. So A is delayed until both siblings are done — hence run once.

PICTURE. The ◇ tree on the left, the flattened wire D→B→C→A→object on the right, with a dotted line showing B's super() skipping sideways to C.

Figure — `super()` — calling parent methods

Step 7 — The marble rolls the whole diamond: D B C A

WHAT. Run D() from Worked Example 3. Each __init__ prints its letter, then calls super():

  • D prints D, cursor → B
  • B prints B, cursor → C (the sideways skip!)
  • C prints C, cursor → A
  • A prints A, cursor → object (does nothing)

Output: D B C A — each exactly once.

WHY not D B A C A? The naive A.__init__(self) inside both B and C would visit A twice. super() follows the single wire, so there is only one A bead to land on.

PICTURE. The marble's full path drawn as a numbered trail 1→2→3→4 across D B C A, with the phantom "double-A" path shown ghosted-out in red to mark what we avoided.

Figure — `super()` — calling parent methods

Step 8 — Degenerate & edge cases (so nothing surprises you)

PICTURE. Three mini-wires side by side: (a) broken run stopping at bead 1 with a red gap, (b) a two-bead single-inheritance wire, (c) the marble resting harmlessly on object.

Figure — `super()` — calling parent methods

The one-picture summary

Everything above compressed: the ◇ tree becomes one wire, the marble rolls left to right printing each letter once, and the state builds bottom-up. If you can read this single frame, you can predict any super() chain.

Figure — `super()` — calling parent methods
Recall Feynman: the whole walk in plain words

Imagine a relay race where the runners are lined up in one fixed order: D, then B, then C, then A, then a finish-line wall called object. When you shout "go" (D()), the first runner does his bit and, instead of naming the next runner, just says "next!" — that's super(). The word "next" always means the person immediately to the right on the line, so nobody runs twice and nobody is skipped. Even in a family shaped like a diamond, Python has already ironed the family tree into this single straight line, so "next" is never ambiguous. If a runner forgets to shout "next," the baton stops and the people down the line never set up their part — that's your missing self.legs. And you never hand the runner your own name (self) because the baton is already in his hand.


Connections