Abstract Base Classes in Python

Learn how to use Python's abc module to define abstract base classes, enforce method implementation in subclasses, and create formal interfaces.

6 min read

Python abstract base classes, provided by the abc module in the standard library, are the language's formal mechanism for defining interfaces that subclasses must implement. An abstract base class, often shortened to ABC, is a class that cannot be instantiated directly. Its purpose is to declare a set of methods that any concrete subclass must provide, and Python enforces this requirement by refusing to create instances of any subclass that leaves an abstract method unimplemented. Where the previous article on abstraction covered the design principle of hiding complexity behind simple interfaces, this article covers the concrete tool that Python provides for formalizing those interfaces and enforcing them at the language level.

The abc module has been part of Python since version 2.6, and it provides two essential components: the ABC class, which you inherit from to mark your class as an abstract base class, and the abstractmethod decorator, which you apply to methods that subclasses must override. Together, they turn the informal Python pattern of raising NotImplementedError in a parent class method into a language-level contract. The difference is not just stylistic. With NotImplementedError, you discover a missing implementation only when the method is called, which might happen deep in a test run or, worse, in production. With an ABC, Python refuses to create an instance of the subclass at all if any abstract method is unimplemented, catching the error at the earliest possible moment.

The article on abstraction in Python explained introduced the idea of defining a parent class that declares what methods must exist without providing implementations. Abstract base classes make that idea concrete and enforceable. They also integrate with Python's built-in isinstance and issubclass functions, which means you can check whether an object conforms to an abstract interface without calling any of its methods. This capability is particularly valuable in large systems where objects pass through many layers of code and verifying their capabilities early prevents confusing errors later.

Defining and using an abstract base class

Creating an ABC requires two steps: inherit from the ABC class and decorate at least one method with abstractmethod. The ABC class, imported from the abc module, marks your class as abstract. The abstractmethod decorator marks individual methods as required. A class that inherits from an ABC but does not implement all abstract methods is itself abstract and cannot be instantiated. A class that implements every abstract method is concrete and can be instantiated normally.

Here is an ABC that defines the interface for a payment processor and two concrete subclasses that implement it:

pythonpython
from abc import ABC, abstractmethod
 
class PaymentProcessor(ABC):
    @abstractmethod
    def authorize(self, amount):
        pass
 
    @abstractmethod
    def capture(self, transaction_id):
        pass

The ABC declares two abstract methods that any concrete subclass must implement to be instantiated. Here is a credit card processor that fulfills the contract:

pythonpython
class CreditCardProcessor(PaymentProcessor):
    def authorize(self, amount):
        return f"CC authorized for ${amount:.2f}"
 
    def capture(self, transaction_id):
        return f"CC captured for {transaction_id}"

And here is a PayPal processor that provides the same two methods with different payment processing logic behind each one:

pythonpython
class PayPalProcessor(PaymentProcessor):
    def authorize(self, amount):
        return f"PayPal authorized for ${amount:.2f}"
 
    def capture(self, transaction_id):
        return f"PayPal captured for {transaction_id}"

The PaymentProcessor ABC declares two abstract methods: authorize and capture. Neither method has a body beyond pass because the ABC does not provide a default implementation. The CreditCardProcessor and PayPalProcessor classes each implement both methods with their own logic. You can create instances of the concrete classes, but attempting to create a PaymentProcessor directly raises a TypeError.

If a developer creates a third processor class and forgets to implement capture, Python catches the mistake when they try to instantiate it, not later when some unrelated code calls capture on the object. The error message includes the name of the missing abstract method, which makes debugging straightforward.

Abstract base classes with concrete methods

An ABC does not need to consist entirely of abstract methods. It can provide concrete method implementations that subclasses inherit and use as-is. This pattern is common when the ABC can provide useful default behavior for some methods while requiring subclasses to implement the methods that vary. The collections.abc module in the standard library uses this pattern extensively: a Mapping ABC provides concrete implementations of keys, values, and items based on a single abstract method that subclasses must implement.

The following example shows an ABC that provides a concrete summary method while requiring subclasses to implement the data retrieval logic:

pythonpython
from abc import ABC, abstractmethod
 
class ReportGenerator(ABC):
    @abstractmethod
    def fetch_data(self):
        pass
 
    def generate_summary(self):
        data = self.fetch_data()
        total = len(data)
        return f"Report contains {total} records"

The fetch_data method is abstract and must be implemented by any concrete subclass. The generate_summary method is concrete and provides a default implementation that calls fetch_data and formats a summary string. Subclasses can use generate_summary as-is or override it if they need a different summary format. This pattern gives you the best of both worlds: the ABC enforces the essential interface through abstract methods while providing shared convenience behavior through concrete methods.

ABCs and isinstance checks

One of the advantages of using formal ABCs over informal NotImplementedError patterns is that ABCs participate in Python's type-checking machinery. You can use isinstance to check whether an object is an instance of a class that implements the ABC's interface, even if that class does not directly inherit from the ABC. Python supports a mechanism called virtual subclassing through the register method, which lets you declare that a class implements an ABC's interface without actually inheriting from it.

In practice, virtual subclassing is used mainly by the standard library's ABCs. The collections.abc module defines ABCs like Sequence, Mapping, and Iterable, and built-in types like list and dict are registered as virtual subclasses of these ABCs. You can write isinstance(my_list, Sequence) and receive True even though list does not inherit from Sequence in its class definition. This capability makes ABCs a powerful tool for checking structural conformance at runtime, complementing the duck typing approach that Python uses by default.

For application code, direct inheritance from an ABC is usually the right choice because it is simpler and more explicit. Virtual subclassing is an advanced feature that is useful primarily when you need to declare that a class you do not control, such as a class from a third-party library, conforms to your interface. The article on duck typing in Python covers the tradeoffs between structural and nominal type checking.

When to use ABCs and when to keep it simple

Abstract base classes add ceremony to your code, and ceremony is not always an improvement. For a small class hierarchy with two or three subclasses that you control, the informal pattern of raising NotImplementedError in the parent class is often sufficient. The error message is clear, the fix is obvious, and the additional imports and decorators of the abc module add complexity without adding value. ABCs earn their keep in larger systems where the interface is implemented by many classes, possibly written by different developers, and where catching missing implementations at instantiation time rather than at call time prevents bugs from reaching production.

ABCs are also valuable when you need isinstance checks against an interface. If your code receives objects from external sources and needs to verify that they support a particular set of methods before using them, an ABC with isinstance is more reliable than checking for individual methods with hasattr. The isinstance check confirms that the object implements the full interface, not just that it happens to have a method with the right name.

The article on class relationships in Python covers the broader landscape of how classes can relate to each other, including inheritance, composition, and association. ABCs are one way to formalize those relationships when you need the guarantees that formal interface enforcement provides.

Rune AI

Rune AI

Key Insights

  • An abstract base class inherits from ABC and uses @abstractmethod to declare methods that subclasses must implement.
  • Python prevents instantiation of any class that has unimplemented abstract methods, catching errors early.
  • ABCs can contain both abstract methods that subclasses must override and concrete methods that subclasses inherit.
  • The abc module integrates with isinstance and issubclass, letting you check interface conformance at runtime.
  • Use ABCs for frameworks and libraries; use informal interfaces with NotImplementedError for simpler application code.
RunePowered by Rune AI

Frequently Asked Questions

What is an abstract base class in Python?

An abstract base class, or ABC, is a class that cannot be instantiated directly and exists to define a common interface that its subclasses must implement. It is created by inheriting from the ABC class in the abc module and marking one or more methods with the @abstractmethod decorator. Subclasses that do not implement all abstract methods cannot be instantiated, giving you a language-level guarantee that every concrete subclass provides the required methods.

How is an ABC different from a regular class that raises NotImplementedError?

A regular class with methods that raise NotImplementedError relies on runtime to detect missing implementations, and the error only occurs when the unimplemented method is called. An ABC with abstract methods prevents instantiation of the subclass entirely if any abstract method is missing, catching the error earlier and at a more useful point. ABCs also integrate with Python's isinstance and issubclass checks, letting you verify that an object conforms to an interface without calling its methods.

Can an abstract base class have concrete methods?

Yes, an ABC can provide concrete method implementations alongside abstract ones. Subclasses inherit the concrete methods and only need to override the abstract ones. This pattern lets you provide shared functionality in the ABC while forcing subclasses to implement the parts that vary. Many of Python's built-in ABCs in the collections.abc module use this pattern to provide rich default behavior based on a small number of abstract methods.

Conclusion

Abstract base classes give you a formal mechanism for defining interfaces and enforcing their implementation. They are the right tool when you are building a framework or library where subclasses must provide specific methods, when you need isinstance checks against an interface rather than a concrete class, or when you want to catch missing method implementations at instantiation time rather than at call time. For everyday application code, informal interfaces based on convention are often sufficient, but knowing how to reach for ABCs when you need them is an essential OOP skill.