Using the Python abc Module for Abstract Base Classes
python abc module: Use the Python abc module to define abstract base classes, enforce method contracts, and control runtime checks with abstractmethod and ABCMeta.
The python abc module gives you a standard way to define abstract base classes and enforce interface contracts at runtime. Instead of relying on documentation alone, you can mark methods as required and let Python refuse to instantiate a class that does not implement them. This is useful when you are building a framework, a plugin system, or a family of related classes that must share a common shape.
The Core Pieces of the abc Module
The module provides a small set of building blocks. ABC is a helper base class that uses the ABCMeta metaclass. abstractmethod is a decorator that marks a method as required. register is a method on ABCMeta that lets you declare an external class as a virtual subclass without changing its inheritance.
| Component | Purpose | Typical Use |
|---|---|---|
ABC | Base class with ABCMeta | Inherit to make a class abstract |
ABCMeta | Metaclass that tracks abstract methods | Custom metaclass scenarios |
abstractmethod | Decorator marking a method as required | Force subclasses to implement |
register | Method to declare a virtual subclass | Add external classes to a hierarchy |
ABC is the easiest entry point. Most code only needs ABC and abstractmethod.
Defining an Abstract Base Class with ABC
Start by inheriting from ABC and decorating the methods that must be implemented.
from abc import ABC, abstractmethod class PaymentProcessor(ABC): @abstractmethod def process(self, amount: float) -> str: """Process a payment and return a receipt id."""
Any subclass that does not implement process cannot be instantiated. Python raises TypeError when you try to create an object from the incomplete class.
class CashProcessor(PaymentProcessor): pass # TypeError: Can't instantiate abstract class CashProcessor with abstract method process
The error appears at instantiation time, not at class definition time. This means the contract is enforced only when someone actually builds an object. That is usually the right moment, because it is when the missing implementation would become a problem.
Using abstractmethod with Properties and Classmethods
abstractmethod works with other decorators, but the order matters. To define an abstract property, put @property above @abstractmethod.
class Config(ABC): @property @abstractmethod def timeout(self) -> int: """Return the timeout in seconds."""
A subclass must override timeout as a property. If it only defines a plain method, Python will still consider the abstract property unimplemented.
For class methods, place @classmethod above @abstractmethod.
class Serializer(ABC): @classmethod @abstractmethod def from_bytes(cls, data: bytes) -> "Serializer": """Create an instance from raw bytes."""
The decorator order is important because abstractmethod needs to see the original function object before classmethod or property wraps it.
Custom ABCs with ABCMeta
When you need a custom metaclass, inherit from ABCMeta directly instead of using ABC.
from abc import ABCMeta, abstractmethod class PluginMeta(ABCMeta): def __new__(mcls, name, bases, namespace): cls = super().__new__(mcls, name, bases, namespace) cls.plugin_name = name.lower() return cls class Plugin(metaclass=PluginMeta): @abstractmethod def run(self): ...
Here Plugin uses PluginMeta as its metaclass, and PluginMeta inherits from ABCMeta, so abstract method tracking still works. ABC is simply a class that already has ABCMeta as its metaclass. If you do not need custom metaclass behavior, prefer ABC.
Runtime Checks with isinstance and issubclass
Abstract base classes also provide runtime type checking. isinstance and issubclass work with ABCs even for classes that do not inherit from them, through register.
class Quackable(ABC): @abstractmethod def quack(self): ... class Duck: def quack(self): return "quack" Quackable.register(Duck) print(isinstance(Duck(), Quackable)) # True
This is useful when you want to accept objects from external libraries without forcing them to inherit from your ABC. The registered class is treated as a virtual subclass, but it does not appear in the actual MRO.
You can also customize the check by overriding __subclasshook__ on your ABC. This gives you full control over what issubclass returns, but it also makes the behavior harder to reason about. Use it sparingly.
Common Mistakes and How to Avoid Them
The most common mistake is using @abstractmethod without inheriting from ABC or setting ABCMeta as the metaclass. Without the metaclass, the decorator has no effect.
from abc import abstractmethod class Broken: @abstractmethod def run(self): ... # This works, which is probably not what you intended. Broken().run()
The class is instantiable because Broken does not have ABCMeta as its metaclass. Always inherit from ABC or use ABCMeta explicitly.
Another mistake is assuming Python will check method signatures. It will not. A subclass can implement an abstract method with a different number of parameters, and Python will accept it. The ABC mechanism only checks that the name exists and is not marked abstract. Keep signatures consistent through careful design and type hints.
Runtime Cost and Maintainability Considerations
Using ABCMeta adds a small amount of work at class creation time because the metaclass must scan the namespace for abstract methods. This cost is negligible for normal class definitions. The more important cost is in isinstance and issubclass checks. When an ABC defines __subclasshook__, Python calls that hook for every check, so a complex hook can slow down code that runs frequently. If you rely on register, the check is resolved through the ABC's internal registry after the initial registration.
Maintainability is where abc earns its place. A clear abstract base class communicates the required interface to every developer who works with the codebase. It also prevents incomplete implementations from being created in the first place. The tradeoff is that an ABC adds an inheritance relationship, and too many abstract layers can make a codebase harder to follow.
When to Use abc Instead of Duck Typing
Use abc when you have multiple implementations that must satisfy the same contract and you want the interpreter to enforce it. This is common in plugin systems, adapters, and strategy patterns. It is also useful when you want to provide a clear base class for third-party extensions.
Avoid abc when you only have one implementation, or when the interface is small and the codebase relies on dynamic attributes. Duck typing can be simpler and more flexible. For example, a function that only calls obj.quack() does not need a Quackable ABC unless you also need isinstance checks or want to prevent incomplete classes from being constructed.
The decision comes down to whether you need the runtime guarantee. If you need it, abc is the standard tool. If you do not, a well-named protocol or a simple base class may be enough.