AiTechWorlds
AiTechWorlds
University learning companion
Object-Oriented Programming (OOP) · super() and Cooperative Multiple Inheritance
A small product team needs to apply “super() and Cooperative Multiple Inheritance” to a feature that real people depend on. Define the problem, state the assumptions, work through one concrete example, and decide how the team would test the result and recognise failure.
Use this course-level map to place “super() and Cooperative Multiple Inheritance” in the larger system. It complements—not replaces—a topic-specific worked diagram.
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 Labradorsuper().__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 chainsvc = 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: 60sEach 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 skippedThe 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__().University learning companion
Object-Oriented Programming (OOP) · super() and Cooperative Multiple Inheritance
Complete the steps without looking at the answer first; record evidence rather than only a final result.
These are primary or established learning references for this subject. Check the relevant chapter or specification for the exact topic and version.
Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises