chapter 11.1

Inheritance

Subclassing, super(), and method overriding

Inheritance lets you create a new class based on an existing one. The new class (child) inherits all the attributes and methods of the original (parent), and can add or override them.
class Animal:
    def __init__(self, name, sound):
        self.name = name
        self.sound = sound

    def speak(self):
        print(f"{self.name} says {self.sound}!")

class Dog(Animal):    # Dog inherits from Animal
    def fetch(self):
        print(f"{self.name} fetches the ball!")

class Cat(Animal):    # Cat inherits from Animal
    def purr(self):
        print(f"{self.name} purrs...")

rex = Dog("Rex", "Woof")
rex.speak()    # Rex says Woof!    (inherited from Animal)
rex.fetch()    # Rex fetches the ball!  (Dog-only method)

luna = Cat("Luna", "Meow")
luna.speak()   # Luna says Meow!   (inherited from Animal)
luna.purr()    # Luna purrs...     (Cat-only method)
Overriding methods — a child class can replace a parent's method with its own version:
class Animal:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return f"Animal: {self.name}"

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)   # Call parent's __init__
        self.breed = breed       # Add new attribute

    def describe(self):          # Override parent's method
        return f"Dog: {self.name} ({self.breed})"

rex = Dog("Rex", "German Shepherd")
print(rex.describe())   # Dog: Rex (German Shepherd)
⚠warning
super() calls the parent class's version of a method. Use it in __init__ when you want to keep the parent's setup and add to it. Forgetting super().__init__() means the parent's attributes never get created.
A more complete example — a game character hierarchy:
class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def take_damage(self, amount):
        self.hp -= amount
        if self.hp <= 0:
            self.hp = 0
            print(f"{self.name} has been defeated!")
        else:
            print(f"{self.name} takes {amount} damage. HP: {self.hp}")

    def __str__(self):
        return f"{self.name} (HP: {self.hp})"

class Warrior(Character):
    def __init__(self, name):
        super().__init__(name, hp=150)  # Warriors get more HP
        self.armor = 10

    def take_damage(self, amount):
        reduced = max(0, amount - self.armor)  # Armor reduces damage
        print(f"{self.name}'s armor blocks {amount - reduced} damage!")
        super().take_damage(reduced)  # Call parent's take_damage

class Mage(Character):
    def __init__(self, name):
        super().__init__(name, hp=80)   # Mages are fragile
        self.mana = 100

    def cast_spell(self, target, damage):
        if self.mana >= 20:
            self.mana -= 20
            print(f"{self.name} casts a fireball!")
            target.take_damage(damage)
        else:
            print("Not enough mana!")

w = Warrior("Kael")
m = Mage("Lyra")

print(w)  # Kael (HP: 150)
m.cast_spell(w, 30)
print(w)  # Kael (HP: 130) — armor blocked some
You can check inheritance relationships with isinstance() and issubclass():
print(isinstance(w, Warrior))    # True
print(isinstance(w, Character))  # True (Warrior IS a Character)
print(isinstance(w, Mage))       # False

print(issubclass(Warrior, Character))  # True
print(issubclass(Mage, Character))     # True
your turn
challenge

Using the `Character` base class, create: 1. A `Warrior` subclass with extra `armor` attribute and overridden `take_damage` that reduces damage by armor 2. A `Healer` subclass with a `heal(target, amount)` method that increases a target's HP Use `super()` in both subclasses' `__init__`.

loading editor...

output
$ Press RUN to execute your code
AI Tutor

Ask about Inheritance

◇

Hey! I'm your AI tutor.

Ask me anything about this lesson, or paste code you're stuck on.

AI tutor is a premium feature