Abstract Base Classes
Use abc.ABC to define a contract that subclasses must fulfil — enforced at instantiation time.
An abstract base class (ABC) defines a contract: it declares methods that must exist, but leaves the implementation to subclasses. Try to instantiate a class that has not implemented all the abstract methods and Python raises a TypeError immediately — no silent half-built objects.
The recipe
Inherit from abc.ABC and decorate the required methods with @abstractmethod.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
...
# Shape() -> TypeError: Can't instantiate abstract class
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side ** 2
print(Square(4).area()) # 16Why bother?
ABCs give you fail-fast guarantees. If a teammate adds a new Shape subclass and forgets area(), they find out the moment they try to create one — not three modules later when something calls the missing method. You can also mix in concrete helper methods that build on the abstract ones.
Example
When to use it
- A plugin system defines an abstract Exporter base class so all plugins must implement export().
- A storage layer defines an abstract Repository with find() and save() that SQL and Redis backends implement.
- A payment gateway enforces that every provider class implements charge() and refund() or fails at instantiation.
More examples
Define and implement an ABC
Defines an abstract Shape class; instantiating it directly fails, but Square implements the required area().
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...
class Square(Shape):
def __init__(self, side): self.side = side
def area(self): return self.side ** 2
print(Square(4).area()) # 16
# Shape() -> TypeError: Can't instantiate abstract classMultiple abstract methods
Shows a Repository ABC with two abstract methods that the InMemoryRepo must implement.
from abc import ABC, abstractmethod
class Repository(ABC):
@abstractmethod
def find(self, id: int): ...
@abstractmethod
def save(self, obj) -> None: ...
class InMemoryRepo(Repository):
def __init__(self): self._store = {}
def find(self, id): return self._store.get(id)
def save(self, obj): self._store[obj['id']] = obj
repo = InMemoryRepo()
repo.save({'id': 1, 'name': 'Alice'})
print(repo.find(1))isinstance check with ABC
Demonstrates that isinstance() returns True for the ABC, enabling type-safe dispatch.
from abc import ABC, abstractmethod
class Serialisable(ABC):
@abstractmethod
def to_dict(self) -> dict: ...
class User(Serialisable):
def __init__(self, name): self.name = name
def to_dict(self): return {'name': self.name}
u = User('Bob')
print(isinstance(u, Serialisable)) # True
print(u.to_dict()) # {'name': 'Bob'}
Discussion