Course contents

Inheritance

Sign in to run this lab and save your progress

Learn

Before you start

Inheritance is easy to misuse. This lesson teaches HOW to use it correctly. The NEXT lesson (composition vs inheritance) teaches WHEN NOT to reach for it.

The one-line version

Inheritance lets a child class reuse the parent's data and methods, and change what it needs to.

Why every design problem needs this

Almost every design problem you'll see has a hierarchy in it:

  • Parking Lot — Vehicle parent, Car / Bike / Truck children with different spot sizes and fees.
  • Vending Machine — Product parent, Snack / Beverage children with different rules.
  • Chess — Piece parent, Pawn / Knight / Bishop / etc. children with different movement rules.
  • File System — Node parent, File / Directory children that behave differently.

Every one of these problems asks you to write a method (like price() or move()) once on the parent class, then let children replace it. Other code holds a list[Vehicle] and doesn't care which kind of vehicle is inside.

That's called "polymorphism" — you'll meet the word in lesson 5. You're already using it every time you loop through a mixed list of vehicles and call .describe().

What super() does

Two things:

  1. In __init__, it calls the parent's setup function. Without this call, the parent's setup code never runs. That means self.plate and self.wheels never get stored. Every downstream test breaks.

  2. In an override, it lets you REUSE what the parent did. super().describe() runs the parent's version. You can add to the result instead of writing the whole thing from scratch.

If you never call super(), you're probably better off with composition (next lesson).

What NOT to do with inheritance

Books have entire chapters on this. Short version:

  • Don't use inheritance just to reuse code. If Truck doesn't behave like a Vehicle, don't make it a child of Vehicle just to borrow Vehicle's methods. Use composition.
  • Don't build deep hierarchies. More than 2-3 levels is usually a sign something's wrong. Interviewers notice.
  • Watch out for "fragile base class". When you change the parent, all the children might break in ways you didn't expect.

We'll draw the line in the next lesson. For today, just get comfortable with super(), class Child(Parent):, and overriding methods.

Your task

Some classes are just variations of another. A Car and a Motorcycle are both vehicles — they share most of their data and behavior and differ in only a few things. Inheritance is how you say "X is a special kind of Y" in code.

class Vehicle:
    def __init__(self, plate, wheels):
        self.plate = plate
        self.wheels = wheels

    def describe(self):
        return f"{self.plate} ({self.wheels} wheels)"

class Car(Vehicle):
    def __init__(self, plate):
        super().__init__(plate, wheels=4)

Three ideas to understand:

  1. class Car(Vehicle): — Car is a child of Vehicle. It gets every attribute and method from Vehicle for free.
  2. super().__init__(plate, wheels=4) — Car's setup function calls Vehicle's setup function. This is what stores plate and wheels on the object. Without this call, they never get stored.
  3. Override to change behavior. A child class can replace any method the parent has. The child's version runs for children; the parent's runs for parents.
class Motorcycle(Vehicle):
    def __init__(self, plate):
        super().__init__(plate, wheels=2)

    def describe(self):
        # Override to add a bike-specific detail.
        base = super().describe()
        return f"{base} — motorcycle"

Now Car("KA-01-1234").describe() returns "KA-01-1234 (4 wheels)" and Motorcycle("KA-01-9999").describe() returns "KA-01-9999 (2 wheels) — motorcycle". Same method name, different behavior — Python picks the right one based on which class made the object.

What if a class has multiple parents?

Python allows class C(A, B): — a class with two parents. When you call a method, Python has to decide which parent's version to use. It uses a rule called Method Resolution Order (MRO). You don't need the details today.

Rule of thumb: stick to one parent per class. If you ever feel like you want multiple parents, look at composition first (next lesson).

When to use inheritance

Use inheritance when:

  • The child really IS a special kind of the parent (a Car is a Vehicle; a Motorcycle is a Vehicle).
  • The child can be used anywhere the parent can, without surprising the caller.

If either isn't true, use composition instead (next lesson shows why this matters).

Your task

Build a small vehicle hierarchy in main.py:

  1. Vehicle — parent class. __init__(self, plate, wheels) stores both. describe() returns "<plate> (<wheels> wheels)".
  2. Car(Vehicle) — takes only plate. Its setup calls super().__init__(plate, wheels=4). It uses Vehicle's describe() unchanged.
  3. Motorcycle(Vehicle) — takes only plate. Setup calls super().__init__(plate, wheels=2). Overrides describe() to add " — motorcycle" at the end.
  4. Truck(Vehicle) — takes plate and cargo_capacity_kg. super().__init__(plate, wheels=6). Overrides describe() to add " — truck, <capacity>kg" at the end.

The tests check that each subclass reports the right wheels, the right description, and — most importantly — that a mixed list of vehicles can all be described using the same method name. Click Submit to run the tests.

Starter files

Preview only — open the lab to edit and run.

files_dir
03-inheritance-starter