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