Method Overriding
Overview
Method overriding allows a subclass to provide a specific implementation of a method that is already defined in its superclass. When you call the method on a subclass instance, Python uses the subclass's version instead of the parent's. Think of it like a franchise restaurant: the parent company defines a default menu, but a local branch can override specific items to cater to regional tastes. In Python, this is how we specialize behavior while sharing a common interface. You typically override methods to achieve one of two things: - Replace behavior: Provide a completely new implementation without calling the parent. - Extend behavior: Add new logic and call the parent's original method using `super()`. class Parent: def process(self, data): return True class Child(Parent): # TRAP: Adding a mandatory argument changes the signature def process(self, data, config): return True Child().process("data") # TypeError: missing 1 required argument: 'config' When overriding a method, changing the mandatory arguments breaks any code that relies on the parent's interface. This violates the Liskov Substitution Principle, which is covered fully in protocols.
It enables polymorphism, allowing different objects to respond to the same method call in their own specific way without needing complex `if` statements.
Where used: Django class-based views, Pydantic validators, FastAPI dependencies
Why learn this
- Customizing behavior of built-in or framework classes
- Implementing polymorphic designs where multiple classes share an interface but behave differently
Code walkthrough
class User:
def get_role(self):
return 'Guest'
class Admin(User):
def get_role(self):
return 'Administrator'
user = User()
print(user.get_role())
admin = Admin()
print(admin.get_role())
Focus: `def get_role(self):` in `Admin` class intercepts the call
Aha moment
class BaseConfig:
def load(self):
print('Loading defaults')
self.setup()
def setup(self):
print('Base setup')
class AppConfig(BaseConfig):
def setup(self):
print('App setup')
config = AppConfig()
config.load()
Prediction: What is printed when config.load() is called?
Common guess: `Loading defaults` followed by `Base setup`, because `load()` is defined in `BaseConfig`.
Even though `load()` is defined in the parent class, `self` refers to the `AppConfig` instance. So `self.setup()` dynamically resolves to the overridden `setup()` method in the subclass.
Common mistakes
- Forgetting to call super(): Overriding a method replaces the parent method entirely. If you meant to extend rather than replace, you must explicitly call `super().method_name()` inside the overriding method.
- Mismatched method signatures: If the subclass method takes different arguments than the parent method, code that expects the parent interface will crash with a `TypeError` when given a subclass instance (violating the Liskov Substitution Principle).
Glossary
- specialize
- To modify or narrow down a general behavior into a more specific implementation for a particular situation, e.g. overriding `Animal.speak()` in `Dog` to return `'Woof'` instead of a generic sound.
- subclass
- A new class that inherits properties and methods from an existing, parent class, e.g. `class Admin(User)` is a subclass of `User`.
Recall questions
- What happens when a subclass defines a method with the same name as one in its parent class?
- Why do we override methods?
- If you override a method, does the parent's original method automatically execute?
Understanding checks
What is printed when `c.greet()` is called?
`Hello Child` `Hello Parent`
The `Child` class overrides the `greet` method. When called, it first prints `'Hello Child'`, and then explicitly calls the `Parent`'s `greet` method using `super()`, which prints `'Hello Parent'`.
Why might code that uses a subclass crash if the subclass overrides a method with a different number of arguments?
If a method is overridden with a different signature, any code expecting the parent class's original method signature will pass the wrong number of arguments, leading to a `TypeError`.
This violates the Liskov Substitution Principle. A subclass should be able to replace its parent without breaking existing code.
Practice tasks
Override the Payment Process
Modify the starter code by creating a `CreditCardPayment` subclass that inherits from `Payment`. Override its `process()` method to print `'Processing credit card payment'`, completely replacing the parent's behavior.
Challenge
Extend, Don't Replace
Create an `APIHandler` with a `handle_request()` method that prints `'Authenticating request'`. Create a `SecureAPIHandler` subclass that overrides `handle_request()`. It should first call the parent's `handle_request()`, and then print `'Encrypting payload'`.
Continue learning
Previous: Class Creation and Instantiation
Previous: Single inheritance and super()
Previous: Instance, Class, and Static Methods
Next: Duck Typing