ABC Module and Abstract Methods
Overview
The `abc` module provides the `ABC` class and `@abstractmethod` decorator to define Abstract Base Classes. These classes cannot be instantiated directly and force child classes to implement specific methods before they can be instantiated. Think of it like a strict contract or a blueprint: a 'Vehicle' blueprint says you must build an 'engine', but you can't drive the blueprint itself. In Python, if a child class inherits from `ABC` but forgets to override an `@abstractmethod`, Python raises a `TypeError` at instantiation time, not when the method is called. A subclass cannot be instantiated until it implements EVERY `@abstractmethod` — an ABC is an enforced interface contract. When combining `@abstractmethod` with decorators like `@property` or `@classmethod`, it must be the innermost decorator. Placing it on the outside breaks the enforcement: class Broken(ABC): @abstractmethod @property def value(self): pass In this broken example, Python fails to recognize `value` as an abstract method, allowing subclasses to be instantiated without implementing it.
It prevents developers from accidentally creating incomplete subclasses, enforcing a strict API contract across a codebase and catching bugs early.
Where used: Django BaseCommand, Collections.abc
Why learn this
- You can enforce strict interfaces across large teams.
- It enables robust plugin architectures where every plugin is guaranteed to have the required methods.
Code walkthrough
from abc import ABC, abstractmethod
class Worker(ABC):
@abstractmethod
def work(self):
pass
class LazyWorker(Worker):
pass
try:
w = LazyWorker()
except TypeError as e:
print(e)
Focus: `w = LazyWorker()` # Fails because `LazyWorker` didn't implement `work()`
Aha moment
from abc import abstractmethod
class FakeAbstract:
@abstractmethod
def do_something(self):
pass
obj = FakeAbstract()
print('Created successfully!')
Prediction: What happens when we instantiate `FakeAbstract`?
Common guess: It raises a `TypeError` because of the abstract method.
It prints `'Created successfully!'`. The `@abstractmethod` decorator only enforces instantiation rules if the class also inherits from `ABC`. Without `ABC`, the decorator does nothing.
Common mistakes
- Forgetting to inherit from ABC: Decorating a method with `@abstractmethod` does nothing if the class doesn't inherit from `ABC` (or use `ABCMeta`). You can still instantiate it.
- Calling the abstract class: Trying to instantiate the abstract base class itself will raise a `TypeError` if it has any abstract methods.
- Wrong decorator order with @abstractmethod: When combining decorators, `@abstractmethod` must be the innermost decorator. If placed above `@classmethod` or `@property`, Python will not enforce the abstract method contract, and no `TypeError` will be raised when instantiating an incomplete subclass.
Recall questions
- What happens if you try to instantiate a class that inherits from `ABC` and contains an `@abstractmethod`?
- What is the purpose of the `@abstractmethod` decorator?
- When does Python check if the abstract methods have been implemented: at class definition, at instantiation, or when the method is called?
- What is the correct decorator order when combining `@abstractmethod` with `@classmethod`?
Understanding checks
What happens when this code is executed?
It prints `'Woof!'`.
The `Animal` class does not inherit from `ABC`, so the `@abstractmethod` decorator has no enforcing effect. The `Dog` class can be instantiated normally.
What happens when this code is executed?
A `TypeError` is raised at `d = Dog()`.
Because `Animal` inherits from `ABC` and `speak` is decorated with `@abstractmethod`, `Dog` must implement `speak` before it can be instantiated.
What happens when this code is executed?
It prints `'Success'`.
Because `@abstractmethod` is placed outside of `@classmethod`, it is ignored. `Child` can be instantiated without implementing `my_method`. To enforce the contract, `@abstractmethod` must be the innermost decorator.
Practice tasks
Enforce a Contract
Currently, `PaymentGateway` is just a regular class, and `StripeGateway` inherits from it but doesn't implement `process_payment`. Modify `PaymentGateway` to inherit from `ABC` and decorate `process_payment` with `@abstractmethod`. Then implement `process_payment` in `StripeGateway` so that it returns a string.
Challenge
Implement an Exporter Interface
Create an abstract base class `DataExporter` with an abstract method `export(self, data)`. Create a subclass `JSONExporter` that implements `export` by returning the data wrapped in a string like `'JSON: [data]'`. Create another subclass `CSVExporter` that forgets to implement `export`. Attempting to instantiate `CSVExporter` should fail with a `TypeError`.
Continue learning
Previous: Class Creation and Instantiation
Previous: Single inheritance and super()
Previous: Method Overriding