2.1.8 · D1OOP Fundamentals

Foundations — Inheritance — single inheritance, method resolution order (MRO)

1,962 words9 min readBack to topic

This page assumes you have seen none of the words on the OOP Fundamentals parent. We build every symbol from the ground up, in the exact order they depend on each other.


1. What is an object? (the very first picture)

Figure — Inheritance — single inheritance, method resolution order (MRO)

Look at the box in the figure. The data lives inside (name = "Rex"). The buttons on the side (speak()) are the behaviors. When you press a button, the object does something, often using its own inside data.


2. What is a class? (the blueprint the box was stamped from)

  • The class is the cutter (exists once, describes the shape).
  • Each object is a cookie (exists many times, holds its own values).

In the parent code, Animal is the class; d = Dog("Rex") makes one object d.

Cloze check:

Is Animal a class or an object?
A class (the blueprint).
Is d a class or an object?
An object (one thing stamped from Dog).

3. The . (dot) — "reach inside / press a button"

  • d.name → reach into d, read the data called name.
  • d.speak() → reach into d, press the button speak, run it (the () = "press now").

The dot is the only way in the language to ask an object for something. Every hard question on the parent page ("which speak runs?") is really the question "where does the dot look?"


4. self — "the object I am attached to"

Figure — Inheritance — single inheritance, method resolution order (MRO)

In the figure, three cookies (d1, d2, d3) share one speak recipe. When you press d2.speak(), inside that run self is d2, so self.name reads d2's name. Press it on d1 and self becomes d1. The recipe is written once; self swaps to whoever pressed the button.


5. __init__ — the "set-up on first stamp" button

class Animal:
    def __init__(self, name):   # runs at birth of the object
        self.name = name        # store the name INTO this object
  • Animal("Rex") → Python stamps a fresh box, then runs __init__ with name="Rex".
  • self.name = name → write "Rex" into this box's name slot.

6. __dict__ — "the class's private drawer of buttons"

Picture a labelled drawer per class:

  • Dog.__dict__ contains speak (Dog wrote it).
  • Dog.__dict__ does not contain __init__ (Animal wrote that one).

Cloze check:

Where does Dog.__init__ live if only Animal defined it?
In Animal.__dict__, not Dog.__dict__.

7. Parent / child, and the arrow that stacks blueprints

The is-a test decides when to use it:

  • A Dog is-a Animal → inherit.
  • A Car has-a Engine → do not inherit; hold it as data (see Composition over Inheritance).
Figure — Inheritance — single inheritance, method resolution order (MRO)

The figure shows the blueprint stack: Dog sits on top of Animal, which sits on object. An object of type Dog can reach every drawer below it.


8. object — the blueprint at the very bottom

So the stack never floats — it always lands on object. That is why the parent's chains always end ..., object. More at object — the root class.

Read \to as "inherits from / sits on top of."


9. Override and extend — two ways a child changes a button

To extend, you need one more tool.


10. super() — "the next blueprint down, please"

class Puppy(Dog):
    def speak(self):
        return super().speak() + " (tiny)"   # run Dog.speak, then add
  • super().speak() → skip Puppy, take the next drawer with speakDog.speak"Woof".
  • then append " (tiny)".

11. The MRO list and __mro__ — the stack written out as a line

And the lookup rule that uses it:

In words: walk the line from the top; the first drawer that has the name wins; stop. If you fall off the end (object included) with no match, Python raises an AttributeError.

The general algorithm behind that formula is C3 Linearization; for single inheritance it collapses to "just the chain," which is all we need here.

Related ideas that ride on this same machinery: Polymorphism and Method Overriding (why the object's stack wins) and Encapsulation and Attribute Lookup (how data names use the same search).


Prerequisite map

object - a box of data plus buttons

class - the blueprint

the dot - reach inside

self - the object I am on

__init__ - set up on birth

__dict__ - a class own drawer

parent and child - is-a stacking

object class - bottom of every stack

super - next blueprint down

MRO - the stack as a line

Inheritance and MRO


Equipment checklist

Test yourself — cover the right side, answer, then reveal.

An object is
a bundle of data plus behavior; a labelled box with buttons.
A class is
the blueprint/recipe an object is stamped from.
obj.thing (the dot) means
reach into obj and get thing; add () to press a button.
self refers to
the particular object the method was pressed on.
__init__ runs
automatically at object creation, to fill in starting data.
Cls.__dict__ contains
only the names this class itself defined, not inherited ones.
class Dog(Animal) makes Animal the
parent (base); Dog the child (derived).
object is
the root class every Python class ultimately inherits from; always last in the MRO.
Override means
replace the parent's method entirely.
Extend means
call the parent's method (via super()) then add to it.
super() means
start lookup at the blueprint just below the current one.
The MRO is
the ordered list of classes searched, most-specific first, ending in object.
The lookup rule is
first class in type(obj).__mro__ whose __dict__ has the name wins.

Ready? Every symbol above appears on the parent topic — you can now read it with nothing undefined. Prefer Hinglish? See 2.1.08 Inheritance — single inheritance, method resolution order (MRO) (Hinglish).