Inheritance, MRO, super() and Duck Typing
45 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Inheritance questions move quickly from "what is it" to "draw the MRO". Know how super() follows the MRO, and know when duck typing makes inheritance unnecessary.
Inheritance and overriding
Inheritance lets a subclass take the attributes and methods of a parent class, so common code is written once and the subclass adds or overrides parts.
A subclass reuses a parent class and changes only what differs. Redefining a method with the same name in the subclass is overriding.
class Account: def __init__(self, balance): self.balance = balance
def monthly_fee(self): return 50
class Savings(Account): def monthly_fee(self): return 0
print(Account(100).monthly_fee(), Savings(100).monthly_fee())Example 1 · Override and isinstance
class Account: def label(self): return "account"
class Savings(Account): def label(self): return "savings"
s = Savings()print(s.label())print(isinstance(s, Account), isinstance(s, Savings))print(issubclass(Savings, Account))savings True True True
What this code does
- The method found on
Savingswins over the one onAccount. isinstanceis True for the class and for every parent.
Example 2 · Calling the parent with super()
class Employee: def __init__(self, name): self.name = name
def describe(self): return f"Employee {self.name}"
class Manager(Employee): def __init__(self, name, team_size): super().__init__(name) self.team_size = team_size
def describe(self): return super().describe() + f" leading {self.team_size}"
print(Manager("Asha", 4).describe())Employee Asha leading 4
What this code does
super().__init__(name)lets the parent set upname, so nothing is copied.super().describe()reuses the parent's text and adds to it.
Savings inherits __init__ and replaces monthly_fee. Python picks the method from the object's own class first.
super() and the MRO
MRO, the method resolution order, is the list of classes Python searches for an attribute. It is built by C3 linearisation and shown by Class.__mro__.
super() calls the next class in the method resolution order, not always the direct parent. With several parents, the order matters.
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print([c.__name__ for c in D.__mro__])Example 3 · Cooperative super() in a diamond
class A: def hello(self): return ["A"]
class B(A): def hello(self): return ["B"] + super().hello()
class C(A): def hello(self): return ["C"] + super().hello()
class D(B, C): def hello(self): return ["D"] + super().hello()
print(D().hello())print([c.__name__ for c in D.__mro__])['D', 'B', 'C', 'A'] ['D', 'B', 'C', 'A', 'object']
What this code does
Bcallssuper()and reachesC, notA, because the MRO ofDis D, B, C, A.- A is visited once, which is the point of the MRO.
Python lists the parents left to right, keeps every class before its own parents, and ends at object. super() follows this list.
Duck typing
Duck typing means an object is used by what it can do, not by its class. If it has the method or attribute the code needs, it is accepted.
Python code asks "can it do this?" instead of "what is it?". Any object with the right method works, with no shared parent needed.
class Circle: def area(self): return 3 * 2 * 2
class Square: def area(self): return 4 * 4
for shape in (Circle(), Square()): print(shape.area())Example 4 · Duck typing and EAFP
class Dog: def speak(self): return "Woof"
class Robot: def beep(self): return "Beep"
for thing in (Dog(), Robot()): try: print(thing.speak()) except AttributeError: print("cannot speak")Woof cannot speak
What this code does
- Neither class shares a parent. The code just tries the call.
- EAFP, easier to ask forgiveness than permission, is the usual Python style.
The loop never checks a type. If an object lacks area, Python raises AttributeError at that call.
Next step
Check your understanding
12 quick questions