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."""
        pass

An 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:

  1. Python checks the __abstractmethods__ attribute of the class.
  2. If this set is non-empty, Python prevents instantiation and immediately raises a TypeError.
  3. 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 execute

To 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."""
        pass

Virtual 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))  # True

Virtual 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.