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