AiTechWorlds
AiTechWorlds
super() and Cooperative InheritancePicture an Olympic 4×100 metre relay team. The first runner explodes off the blocks and sprints their 100 metres. At the exchange zone, they pass the baton to the second runner — not by stopping and handing it over carefully, but at full speed, trusting the next runner to take it without breaking stride. The second runner continues, passes to the third, and so on.
Nobody shouts out "pass to Lane 5, runner number 3!" The handoff is automatic — the next runner in the relay is whoever is positioned next in the order.
super() is Python's relay baton mechanism. When a class calls super(), it does not hardcode "call MyParent's method" — it says "call the next class in the MRO chain." This makes the entire chain cooperative: each class does its part and hands the baton forward, without any class needing to know who comes after it.
super() in Modern Python 3In Python 3, super() with no arguments is the standard form:
class Animal:
def __init__(self, name):
self.name = name
print(f"Animal.__init__ called for {name}")
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # Pass the baton to Animal.__init__
self.breed = breed
print(f"Dog.__init__ added breed: {breed}")
d = Dog("Rex", "Labrador")
print(d.name, d.breed)
Output:
Animal.__init__ called for Rex
Dog.__init__ added breed: Labrador
Rex Labrador
super().__init__(name) is equivalent to calling Animal.__init__(self, name) — but the key difference is that super() is dynamic: it uses the MRO to find the next class, not a hardcoded parent name. This matters enormously in multiple inheritance.
super() With ArgumentsIn Python 2 (and occasionally in Python 3 code that needs to be explicit), you see:
super(Dog, self).__init__(name)
Both Dog (the current class) and self (the current instance) must be passed manually. Python 3's no-argument super() is cleaner and less error-prone — prefer it unless you have a specific reason to use the legacy form.
super() Is Critical in Multiple InheritanceWithout super(), the chain breaks. Look at this broken example:
class Base:
def __init__(self):
print("Base.__init__")
class A(Base):
def __init__(self):
Base.__init__(self) # Hardcoded — NOT using super()
print("A.__init__")
class B(Base):
def __init__(self):
Base.__init__(self) # Hardcoded — NOT using super()
print("B.__init__")
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print("C.__init__")
c = C()
Output (broken — Base runs TWICE):
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__
Base.__init__ is called once through A and once through B. In more serious cases this causes incorrect initialisation, doubled side-effects, or outright errors.
Now fix it with cooperative super():
class Base:
def __init__(self, **kwargs):
print("Base.__init__")
super().__init__(**kwargs) # Passes to object, cleanly terminates
class A(Base):
def __init__(self, **kwargs):
print("A.__init__")
super().__init__(**kwargs) # Passes baton to next in MRO
class B(Base):
def __init__(self, **kwargs):
print("B.__init__")
super().__init__(**kwargs) # Passes baton to next in MRO
class C(A, B):
def __init__(self, **kwargs):
print("C.__init__")
super().__init__(**kwargs) # Starts the chain
c = C()
Output (correct — Base runs ONCE):
C.__init__
A.__init__
B.__init__
Base.__init__
The MRO for C is [C, A, B, Base, object]. Each super().__init__() call walks one step forward in that list. Base runs exactly once.
**kwargs MattersIn a cooperative chain, each class may only need some of the keyword arguments. **kwargs lets each class take what it needs and pass the rest forward, so every class in the chain gets satisfied:
class LogMixin:
def __init__(self, log_level="INFO", **kwargs):
self.log_level = log_level
print(f"LogMixin: log_level={log_level}")
super().__init__(**kwargs) # Pass remaining kwargs forward
class TimingMixin:
def __init__(self, timeout=30, **kwargs):
self.timeout = timeout
print(f"TimingMixin: timeout={timeout}s")
super().__init__(**kwargs) # Pass remaining kwargs forward
class Service:
def __init__(self, name, **kwargs):
self.name = name
print(f"Service: name={name}")
super().__init__(**kwargs) # Should be empty by now
class MonitoredService(LogMixin, TimingMixin, Service):
def __init__(self, **kwargs):
print("MonitoredService: initialising")
super().__init__(**kwargs) # Start the chain
svc = MonitoredService(name="PaymentService", log_level="DEBUG", timeout=60)
print()
print(f"Name: {svc.name}")
print(f"Log level: {svc.log_level}")
print(f"Timeout: {svc.timeout}s")
Output:
MonitoredService: initialising
LogMixin: log_level=DEBUG
TimingMixin: timeout=60s
Service: name=PaymentService
Name: PaymentService
Log level: DEBUG
Timeout: 60s
Each class peeled off its own keyword argument (log_level, timeout, name) and passed the remaining **kwargs forward. No class needed to know about the others.
super()class BrokenLogMixin:
def __init__(self, log_level="INFO"): # No **kwargs — chain stops here
self.log_level = log_level
# No super().__init__() call — baton dropped!
class BrokenService(BrokenLogMixin, Service):
def __init__(self, name, log_level="INFO"):
super().__init__(log_level=log_level) # Service.__init__ never runs
self.name = name # Must set manually — messy
svc = BrokenService("PaymentService")
# Service.__init__ was never called — any logic there is silently skipped
The symptom of a broken chain is silent skipping — no error, but initialisation code in some parent classes never runs.
super() ContractFor cooperative inheritance to work, every class in the chain must follow these rules:
super().__init__(**kwargs) in __init__ (pass it forward, always).**kwargs for any arguments you don't consume yourself.__init__ — let the MRO decide who's next.super() with no arguments is the modern Python 3 style — always prefer it.super() does not mean "call my parent"; it means "call the next class in the MRO".super(), ancestor classes can run multiple times or not at all.**kwargs in every __init__ in a mixin chain so that keyword arguments flow through all the way to object.__init__, call super().__init__().Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises