How to Use Python Abstract Base Classes (abc Module)
Abstract Base Classes (ABCs) in Python establish a formal contract
for class interfaces, ensuring that derived classes implement specific
methods and properties before they can be instantiated. By using
Python's built-in abc module, developers can define
blueprint classes with the ABC class and mark mandatory
interfaces using the @abstractmethod decorator. This
article explains how Python defines these classes, enforces their
implementation at instantiation time via metaclasses, and allows dynamic
type verification with virtual subclasses.
Defining an Abstract Base Class
To create an Abstract Base Class, inherit from the ABC
class provided by the abc module. The ABC
class is a helper class that sets its metaclass to ABCMeta.
Methods that subclasses must implement are decorated with
@abstractmethod.
from abc import ABC, abstractmethod
class Database(ABC):
@abstractmethod
def connect(self) -> None:
"""Connect to the database."""
pass
@abstractmethod
def execute(self, query: str) -> None:
"""Execute a query."""
passAn abstract method can still contain functional logic. Subclasses can
invoke this base implementation using super(), even though
they are still required to override the method.
How Python Enforces the Contract
Python enforces the abstract contract at runtime during class
instantiation, not at definition time. The enforcement relies on the
ABCMeta metaclass, which inspects the class during creation
and tracks all abstract methods in a special internal attribute named
__abstractmethods__.
When a class is instantiated:
- Python checks the
__abstractmethods__attribute of the class. - If this set is non-empty, Python prevents instantiation and
immediately raises a
TypeError. - A subclass only clears this set when it provides concrete overrides
for every method decorated with
@abstractmethod.
class PostgresDatabase(Database):
def connect(self) -> None:
print("Connected to PostgreSQL.")
# Attempting instantiation without defining execute()
db = PostgresDatabase()
# Raises: TypeError: Can't instantiate abstract class PostgresDatabase with abstract method executeTo instantiate PostgresDatabase successfully, all
abstract members must be defined:
class PostgresDatabase(Database):
def connect(self) -> None:
print("Connected to PostgreSQL.")
def execute(self, query: str) -> None:
print(f"Executing: {query}")
# Instantiation succeeds
db = PostgresDatabase()Abstract Properties, Class Methods, and Static Methods
The abc module integrates with other decorators. When
combining @abstractmethod with @property,
@classmethod, or @staticmethod,
@abstractmethod should be applied as the innermost
decorator.
class FileHandler(ABC):
@property
@abstractmethod
def file_extension(self) -> str:
"""Return the target file extension."""
pass
@classmethod
@abstractmethod
def create_handler(cls, file_name: str):
"""Factory method to create an instance."""
passVirtual Subclasses and
register()
Python ABCs also allow structural subtyping (duck typing) alongside
nominal subtyping. You can register an unrelated class as a "virtual
subclass" using the register method or decorator.
@Database.register
class CustomStorage:
def connect(self) -> None:
pass
def execute(self, query: str) -> None:
pass
# Type checks recognize CustomStorage as a subclass
print(issubclass(CustomStorage, Database)) # True
print(isinstance(CustomStorage(), Database)) # TrueVirtual subclasses pass isinstance() and
issubclass() checks, but Python does not check them for
method completeness upon instantiation.
Alternatively, an ABC can define the __subclasshook__
method to customize issubclass() behavior dynamically based
on whether certain method names exist on the target class.