The super() Proxy and Method Resolution Order (MRO)
18 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)When overriding methods in subclasses, you frequently want to extend rather than replace parent functionality. The built-in super() function returns a proxy object that delegates method calls along the class’s Method Resolution Order (MRO).
Calling Parent Constructors with super()
super() is a built-in that returns a proxy for the next class in the Method Resolution Order. Calling a method on it runs that class's version, so a child can extend a parent method instead of replacing it.
Inside a subclass __init__, always call super().__init__(...) to initialize parent attributes before adding subclass-specific state.
class Person: def __init__(self, name, email): self.name = name self.email = email
class Employee(Person): def __init__(self, name, email, employee_id): super().__init__(name, email) # Delegate to Person self.employee_id = employee_id
e = Employee("Aarav", "aarav@corp.com", "E1042")print(f"{e.name} ({e.employee_id})")Example 1 · Extending a parent method with super()
class Account: def __init__(self, balance): self.balance = balance
def withdraw(self, amount): self.balance -= amount return self.balance
class FeeAccount(Account): def withdraw(self, amount): return super().withdraw(amount + 10)
account = FeeAccount(1000)print(account.withdraw(200))print(account.balance)790 790
What this code does
FeeAccount.withdrawadds a fee, then letsAccount.withdrawdo the real work.- The balance drops by 210, not 200, and the parent code is not copied.
super() eliminates hardcoded references like Person.__init__(self, name, email), making your code resilient to class renamings.
Multiple Inheritance in Python
Mixin is a small class that adds one capability, such as logging, to other classes through multiple inheritance. It is not meant to be used on its own.
Python allows a class to inherit from multiple parent classes: class Child(BaseA, BaseB):.
class LoggerMixin: def log(self, message): print(f"[LOG] {message}")
class JSONSerializableMixin: def to_json(self): return str(self.__dict__)
class Order(LoggerMixin, JSONSerializableMixin): def __init__(self, order_id): self.order_id = order_idExample 2 · A mixin adds one capability
class LogMixin: def log(self, message): print(f"[LOG] {self.__class__.__name__}: {message}")
class Order(LogMixin): def __init__(self, order_id): self.order_id = order_id
class Invoice(LogMixin): pass
Order(501).log("created")Invoice().log("sent")[LOG] Order: created [LOG] Invoice: sent
What this code does
LogMixinknows nothing about orders or invoices.- Two unrelated classes gain the same
log()method without sharing a parent.
Mixins are lightweight classes providing modular, reusable capabilities across unrelated classes.
Method Resolution Order (MRO) and C3 Linearization
Method Resolution Order (MRO) is the fixed list of classes Python searches, in order, to find a method or attribute. Python builds it with the C3 rule so a child always comes before its parents.
When multiple parents define the same method, which one runs? Python calculates an unambiguous order called the Method Resolution Order (MRO) using the C3 Linearization algorithm.
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print([cls.__name__ for cls in D.mro()])Example 3 · The MRO decides which method runs
class Base: def greet(self): return "Base"
class Left(Base): def greet(self): return "Left"
class Right(Base): def greet(self): return "Right"
class Child(Left, Right): pass
print(Child().greet())print([cls.__name__ for cls in Child.mro()])Left ['Child', 'Left', 'Right', 'Base', 'object']
What this code does
Childhas nogreet, so Python walks its MRO:Child,Left,Right,Base,object.Leftis listed first in the class line, so its version wins.
Python searches classes in the exact order listed in mro(): child first, left parent, right parent, up to built-in object.
Why super() is Cooperative
In multiple inheritance, super() means "call the next class in the MRO of the object", not "call my parent". Every class in the chain must call super() for the chain to continue.
Example 4 · Each super() call moves one step along the MRO
class A: def action(self): return "A"
class B(A): def action(self): return "B -> " + super().action()
class C(A): def action(self): return "C -> " + super().action()
class D(B, C): def action(self): return "D -> " + super().action()
d = D()print(d.action())D -> B -> C -> A
What this code does
- The MRO for
Dis[D, B, C, A, object]. - Notice inside
B,super().action()callsC.action(), notA! This is cooperative MRO delegation.
Example 5 · Cooperative __init__ with keyword arguments
class Named: def __init__(self, name, **kwargs): super().__init__(**kwargs) self.name = name
class Priced: def __init__(self, price, **kwargs): super().__init__(**kwargs) self.price = price
class Product(Named, Priced): pass
item = Product(name="Desk", price=7500)print(item.name, item.price)print([cls.__name__ for cls in Product.mro()])Desk 7500 ['Product', 'Named', 'Priced', 'object']
What this code does
Namedtakesnameand passes the rest on.Pricedtakespriceand passes the rest on.- The last
super().__init__(**kwargs)reachesobjectwith nothing left over, which is correct.
Each class adds its own part and hands over to the next one. A sits at the end, so it runs exactly once, even though both B and C inherit from it.
Next step
Check your understanding
11 quick questions