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):
base = super().speak() # calls Animal's speak() explicitly
return f"{base}, specifically a bark"
Dog("Rex").speak() # "Rex makes a sound, specifically a bark"
super() gives access to the parent class's version of a method being overridden, without hardcoding the parent class's name — which matters once multiple inheritance is involved, since hardcoding Animal.speak(self) bypasses Python's cooperative-inheritance resolution entirely.
class A:
def greet(self):
return "A"
class B(A):
def greet(self):
return "B -> " + super().greet()
class C(A):
def greet(self):
return "C -> " + super().greet()
class D(B, C):
def greet(self):
return "D -> " + super().greet()
D().greet() # "D -> B -> C -> A"
D.__mro__ # (D, B, C, A, object) — the Method Resolution Order
Python uses the C3 linearization algorithm to compute a single, consistent Method Resolution Order (__mro__) for classes with multiple inheritance — super() doesn't mean "my direct parent," it means "the next class in the MRO," which is why B.greet's super().greet() reaches C before A in the diamond above, rather than jumping straight to A. This is a common point of confusion for anyone coming from a single-inheritance language.