Override and extend methods with super()
A subclass can replace a parent method by defining one with the same name, or extend it: call the parent's version with super() and add to its result. The same goes for __init__.
- Override a method by defining one with the same name in the subclass
- Extend a method with
super().method(...)instead of copying the parent's code - Add attributes in a subclass
__init__that callssuper().__init__(...)first
A method defined in the subclass with the same name as one in the parent overrides it: lookup finds the subclass's version first, so the parent's is not used for those objects.
Often you don't want to replace the parent's work, only add to it. Copying the parent's code into the subclass would duplicate it. super().label() calls the parent's label on the same object, so the subclass can take its result and extend it.
The most common case is __init__. A subclass that needs an extra attribute defines its own __init__, which replaces the parent's completely, so it must call super().__init__(...) to have the parent set up its attributes too.
Run each example to see what it prints. Running examples doesn't count toward progress.
class Product: def __init__(self, sku, name, price): self.sku = sku self.name = name self.price = price def label(self): return f"{self.name} - £{self.price:.2f}" class ShippedProduct(Product): def __init__(self, sku, name, price, weight_kg): super().__init__(sku, name, price) self.weight_kg = weight_kg def label(self): return super().label() + f" ({self.weight_kg} kg)" class DigitalProduct(Product): def label(self): return f"{self.name} - download" for item in [Product("GFT-00001", "Gift wrap", 2.5), ShippedProduct("SPO-11681", "Yoga mat", 31.0, 1.2), DigitalProduct("DIG-30115", "Python Basics (e-book)", 12.0)]: print(item.label())
class Product: def __init__(self, sku, name, price): self.sku = sku self.name = name self.price = price class ShippedProduct(Product): def __init__(self, sku, name, price, weight_kg): self.weight_kg = weight_kg mat = ShippedProduct("SPO-11681", "Yoga mat", 31.0, 1.2) print(mat.weight_kg) print(mat.name)
class Order: def __init__(self, order_id, lines): self.order_id = order_id self.lines = lines def total(self): return round(sum(quantity * price for quantity, price in self.lines), 2) class LoyaltyOrder(Order): def total(self): return round(super().total() * 0.95, 2) class StaffOrder(Order): def __init__(self, order_id, lines, staff_id): super().__init__(order_id, lines) self.staff_id = staff_id def total(self): return round(super().total() * 0.7, 2) lines = [(2, 17.92), (1, 4.99)] for order in [Order("O1", lines), LoyaltyOrder("O2", lines), StaffOrder("O3", lines, "S014")]: print(type(order).__name__, order.total())
When to use it: override when a subclass genuinely does something different; extend with super() when it does the parent's job plus a little more.
Mistakes to avoid: a subclass __init__ without super().__init__(...) (above); copying the parent's method body instead of calling super(); misspelling the method name, which silently adds a new method instead of overriding; and changing what a method returns (a number in the parent, text in the child), which breaks code that uses the parent's contract.
- A method with the parent's name in the subclass overrides it.
super().method(...)runs the parent's version on the same object, so an override can extend instead of copy.- A subclass
__init__must callsuper().__init__(...). - Next: calling the same method on objects of different classes, and letting each class decide.