Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

Lesson 7 of 11

A child class can override a parent method by defining one with the same name. Python finds the child's version first, so that's the one that runs.

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} makes a sound."

class Dog(Animal):
    def speak(self):
        return f"{self.name} says woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says meow!"

print(Animal("Generic").speak())
print(Dog("Rex").speak())
print(Cat("Whiskers").speak())

super() — extending instead of replacing

Overriding throws the parent's version away. Often you want to add to it instead. super() gives you the parent's version.

class Animal:
    def describe(self):
        return "I am an animal."

class Dog(Animal):
    def describe(self):
        base = super().describe()
        return f"{base} Specifically, a dog."

print(Dog().describe())

super().__init__()

This is where super() matters most. A child that defines its own __init__ replaces the parent's — so the parent's setup never runs:

class Animal:
    def __init__(self, name):
        self.name = name

class Dog(Animal):
    def __init__(self, name, breed):
        self.breed = breed

rex = Dog("Rex", "Labrador")
print(rex.breed)
print(rex.name)

That raises AttributeError — nothing ever set self.name. Call the parent's __init__ first:

class Animal:
    def __init__(self, name):
        self.name = name

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed

rex = Dog("Rex", "Labrador")
print(rex.name)
print(rex.breed)

The pattern is always the same: super().__init__(...) first with whatever the parent needs, then the child's own extra attributes.

Why super() and not Animal.__init__(self, name)

Both work, but super() is better:

  • The parent's name isn't hardcoded, so renaming or re-parenting doesn't break the child.
  • You don't pass self manually.
  • It handles multiple inheritance correctly.

A worked example

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def describe(self):
        return f"{self.owner}: {self.balance}"

class SavingsAccount(BankAccount):
    def __init__(self, owner, balance=0, rate=0.02):
        super().__init__(owner, balance)
        self.rate = rate

    def add_interest(self):
        self.balance += self.balance * self.rate

    def describe(self):
        return f"{super().describe()} (savings at {self.rate})"

acct = SavingsAccount("Ada", 1000)
acct.add_interest()
print(acct.balance)
print(acct.describe())

SavingsAccount extends both __init__ and describe(), reusing the parent's work in each rather than duplicating it.

Try it yourself

Override speak() in Dog, and extend describe() with super().

Checkpoint

  • Defining a method with the parent's name overrides it.
  • super() reaches the parent's version so you can extend rather than replace.
  • A child __init__ must call super().__init__(...) or the parent's setup is skipped.
  • Prefer super() over naming the parent class directly.

Questions about this lesson

Log in to ask a question.