Abstract Base Classes (abc Module)
18 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)While duck typing is great for informal polymorphism, large engineering systems require strict interface contracts. Python’s abc module lets you define Abstract Base Classes that raise errors at instantiation time if a subclass forgets to implement required methods.
Defining an Abstract Base Class
Abstract base class (ABC) is a class that lists methods its subclasses must write. You cannot create an object from it, only from a subclass that fills in every required method.
Inherit from ABC and decorate required interface methods with @abstractmethod.
from abc import ABC, abstractmethod
class DatabaseRepository(ABC): @abstractmethod def get_by_id(self, item_id): """Retrieve item by unique identifier"""
@abstractmethod def save(self, item): """Persist item to storage"""
print(DatabaseRepository.__abstractmethods__ == {"get_by_id", "save"})Example 1 · An abstract class cannot be created
from abc import ABC, abstractmethod
class Report(ABC): @abstractmethod def render(self): pass
try: Report()except TypeError as error: print(error)Can't instantiate abstract class Report without an implementation for abstract method 'render'
What this code does
Reporthas an abstract method, so Python refuses to build an object from it.- The message names the class and the missing method.
Trying to create DatabaseRepository() directly raises a TypeError. The class only describes what a repository must do.
Enforcing Implementation in Subclasses
Abstract method is a method marked with @abstractmethod. It names something every concrete subclass must provide. Python refuses to create an object while any abstract method is still missing.
A subclass cannot be instantiated until ALL decorated @abstractmethod methods are overridden.
Example 2 · A subclass that fills in the contract
from abc import ABC, abstractmethod
class Storage(ABC): @abstractmethod def write(self, data): pass
class MemoryStorage(Storage): def __init__(self): self.data = [] def write(self, data): self.data.append(data) return len(self.data)
mem = MemoryStorage()print("Written items:", mem.write("user_payload"))Written items: 1
What this code does
MemoryStorageimplementswrite(), satisfying the abstract contract.meminstantiates and functions cleanly.
Example 3 · A subclass that forgot a method
from abc import ABC, abstractmethod
class DatabaseRepository(ABC): @abstractmethod def get_by_id(self, item_id): pass
@abstractmethod def save(self, item): pass
class IncompleteRepo(DatabaseRepository): def get_by_id(self, item_id): return {"id": item_id}
try: IncompleteRepo()except TypeError as error: print(error)Can't instantiate abstract class IncompleteRepo without an implementation for abstract method 'save'
What this code does
IncompleteRepodefinesget_by_idbut notsave.- Creating it raises
TypeError, and the message names the missing method.
The check happens when you create the object, not when the class is defined. A missing method is reported at once instead of failing later in production.
Providing Concrete Base Methods in ABCs
An abstract class can provide concrete helper methods alongside abstract contracts.
from abc import ABC, abstractmethod
class PaymentGateway(ABC): @abstractmethod def process_charge(self, amount): pass
def audit_log(self, status, amount): print(f"[AUDIT] Status: {status} | Amount: {amount}")
class Cash(PaymentGateway): def process_charge(self, amount): self.audit_log("ok", amount)
Cash().process_charge(250)Example 4 · A concrete method uses abstract steps
from abc import ABC, abstractmethod
class Gateway(ABC): @abstractmethod def charge(self, amount): pass
def pay(self, amount): print("Checking", amount) self.charge(amount) print("Receipt for", amount)
class Upi(Gateway): def charge(self, amount): print("UPI charged", amount)
Upi().pay(750)Checking 750 UPI charged 750 Receipt for 750
What this code does
pay()is written once in the ABC and gives the order of the steps.- Each subclass supplies only
charge(), the step that differs.
Subclasses inherit audit_log() while being required to implement their own process_charge().
Virtual Subclasses with abc.register()
Virtual subclass is a class registered with an ABC through register(). isinstance and issubclass treat it as a subclass, but it inherits nothing and Python does not check its methods.
An ABC can register third-party classes as "virtual subclasses" without them explicitly inheriting from the ABC.
from abc import ABC, abstractmethod
class DatabaseRepository(ABC): @abstractmethod def save(self, data): pass
class ThirdPartyDriver: def save(self, data): print("Saved:", data)
DatabaseRepository.register(ThirdPartyDriver)
driver = ThirdPartyDriver()print(isinstance(driver, DatabaseRepository)) # TrueExample 5 · Registering a class you do not own
from abc import ABC, abstractmethod
class Exporter(ABC): @abstractmethod def export(self, rows): pass
class LegacyExporter: def export(self, rows): return len(rows)
class Unrelated: pass
Exporter.register(LegacyExporter)print(isinstance(LegacyExporter(), Exporter))print(isinstance(Unrelated(), Exporter))print(issubclass(LegacyExporter, Exporter))True False True
What this code does
register()is how an ABC accepts a class whose code you cannot change.- Only the registered class counts.
Unrelatedis not anExporter.
Example 6 · Built-in ABCs you already use
from collections.abc import Iterable, Mapping, Sequence
print(isinstance([1, 2], Sequence))print(isinstance({"a": 1}, Mapping))print(isinstance("text", Iterable))print(isinstance(42, Iterable))True True True False
What this code does
- The
collections.abcmodule holds ABCs that describe common behaviour. - Lists, dicts and strings are registered with them, so
isinstanceworks without inheritance.
isinstance(driver, DatabaseRepository) is True although ThirdPartyDriver never inherited from it. Python trusts the registration and does not check that the methods exist.
Next step
Check your understanding
11 quick questions