← Python lessons

Python super() and Method Overriding Explained

Method overriding is a subclass defining a method with the same name as one in its parent. super() lets the subclass still call the parent's version.

Read Inheritance first.

Override a parent method

When a subclass defines a method its parent also has, the subclass's version wins for its objects.

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

    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return "Woof"

class Cat(Animal):
    def speak(self):
        return "Meow"

for pet in [Dog("Rex"), Cat("Tom"), Animal("Blob")]:
    print(pet.name, "says", pet.speak())

Python finds speak in Dog and Cat first, so it never reaches the one in Animal. A plain Animal still uses its own.

Call the parent's __init__ with super()

A subclass __init__ replaces the parent's __init__. Call super().__init__(...) to run the parent's setup too, then add the new attributes.

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", "Beagle")
print(rex.name, rex.breed)

super() means "the parent class, for this object". super().__init__(name) runs Animal.__init__, which sets self.name.

Forgetting super().__init__()

Leave out the super() call and the parent's setup never runs. Its attributes are missing.

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

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

rex = Dog("Rex", "Beagle")
print(rex.breed)
print(rex.name)  # this line raises AttributeError

breed prints, then rex.name fails. Only Dog.__init__ ran.

Extend a method instead of replacing it

Any overriding method can call super(). Use it to build on the parent's result instead of writing it again.

class Account:
    def __init__(self, balance):
        self.balance = balance

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

class Savings(Account):
    def __init__(self, balance, rate):
        super().__init__(balance)
        self.rate = rate

    def describe(self):
        return super().describe() + f" at {self.rate}% interest"

print(Account(50).describe())
print(Savings(100, 4).describe())

Savings.describe gets "Balance: 100" from the parent, then adds the interest rate.

Note: Write super().__init__(name) with no self inside the brackets. super() already knows which object it is working on.

See also: Methods and self, Custom exceptions.

Your turn

Finish Manager.__init__: call the parent's __init__ with name and pay, then store team_size.

class Employee:
    def __init__(self, name, pay):
        self.name = name
        self.pay = pay

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

class Manager(Employee):
    def __init__(self, name, pay, team_size):
        # add two lines: call the parent __init__, then store team_size on self
        pass

    def describe(self):
        return super().describe() + f" and leads {self.team_size}"

boss = Manager("Ana", 900, 4)
print(boss.describe())

Expected output

Ana earns 900 and leads 4

Practise this in the free Python course, where every exercise checks your code instantly.