Implementing Interfaces in Python: ABCs and Protocols
In object-oriented programming (OOP), an interface describes the methods a class must implement to play a specific role. This set of methods expresses the interface contract. Python has no dedicated syntax for interfaces, but it gives you two mechanisms for modeling them: abstract base classes and protocols. Python’s everyday duck typing takes advantage of both.
By the end of this tutorial, you’ll understand that:
abc.ABCcombined with@abstractmethodenforces the contract at instantiation time, raising aTypeErrorwhen a subclass is missing required methods.typing.Protocoldefines a structural interface that classes satisfy without inheriting from the protocol, verified by static type checkers.- Duck typing treats any object exposing the expected methods as valid, leveraging Python’s dynamic nature.
isinstance()andissubclass()verify contracts at runtime through ABC inheritance, virtual subclass registration, or@runtime_checkableprotocols.
In this tutorial, you’ll write code examples that model interfaces with ABCs and protocols, rely on duck typing for behavior-based contracts, and run explicit isinstance() and issubclass() checks when you need formal verification at runtime.
Get Your Code: Click here to download the free sample code you’ll use to model interfaces with ABCs and protocols.
Take the Quiz: Test your knowledge with our interactive “Implementing Interfaces in Python: ABCs and Protocols” quiz. You’ll receive a score upon completion to help you track your learning progress:
Interactive Quiz
Implementing Interfaces in Python: ABCs and Protocols
Check your understanding of Python interfaces with abstract base classes, protocols, and duck typing, and how to enforce method contracts.
Python’s Approach to Interfaces
In object-oriented programming, an interface acts as a blueprint for implementing concrete classes. Interfaces define methods that are typically abstract, which means that the interface declares them, but doesn’t implement them.
The implementation is the job of any class that implements the interface. By prescribing a set of methods, interfaces let you write code that works with many different types without caring about their concrete implementations.
Python doesn’t have an interface keyword like Java or Go, or a close equivalent like C++’s pure virtual classes. Instead, it offers two techniques for modeling interfaces:
- Inheritance-based interfaces with abstract base classes (ABCs)
- Structural subtyping interfaces with protocols
Python’s approach is just different. Where Java, Go, or C#—each with a dedicated interface keyword—require a class to explicitly declare which interface it implements, Python relies on inheritance or structural matching.
Inheritance-Based Interfaces With Abstract Base Classes (ABCs)
The abc module in Python’s standard library gives you several tools for working with abstract base classes (ABCs) and modeling inheritance-based interfaces. ABCs were Python’s first formal interface mechanism, and they’re built on inheritance. A concrete class declares its intent to satisfy an interface by subclassing the corresponding ABC.
ABCs enforce the interface contract. Any subclass that’s missing a required method fails with a TypeError the moment you try to instantiate it. This makes ABCs a solid choice when you control the hierarchy and want to strictly enforce a specific interface at instantiation time.
Defining an ABC-Based Interface
An ABC declares abstract methods using the @abstractmethod decorator from the abc module. Any subclass must implement every abstract method. Otherwise, Python raises an error the moment you try to instantiate the subclass.
Say you need an interface for file readers. Readers for different formats like PDFs and emails will require the same methods but with different internal logic. The shared contract is to load a file from a path and return the extracted text.
Here’s that interface modeled as an ABC:
Filename:
readers_abc.py
from abc import ABC, abstractmethod
class FileReaderInterface(ABC):
"""Interface for file readers."""
@abstractmethod
def load_file(self, path: str) -> None:
"""Load a file for text extraction."""
@abstractmethod
def extract_text(self) -> str:
"""Return text extracted from the loaded file."""
A few things to notice in this code snippet:
FileReaderInterfaceinherits fromabc.ABC, which sets up the metaclass for you.- Each abstract method is decorated with
@abstractmethod. - The body of abstract methods can contain a descriptive docstring, a bare
passstatement, an ellipsis (...), or raise aNotImplementedErrorexception.
Now, create two concrete classes, PdfReader and EmailReader, which inherit from FileReaderInterface:
Filename:
readers_abc.py
# ...
class PdfReader(FileReaderInterface):
"""Extract text from a PDF."""
def load_file(self, path: str) -> None:
"""Load a PDF file for text extraction."""
print(f"Loading PDF from {path}")
def extract_text(self) -> str:
"""Return text extracted from the loaded PDF."""
return "Extracted PDF text"
class EmailReader(FileReaderInterface):
"""Extract text from an Email."""
def load_file(self, path: str) -> None:
"""Load an EML file for text extraction."""
print(f"Loading email from {path}")
def extract_email_text(self) -> str:
"""Return text extracted from the loaded email."""
return "Extracted email text"
PdfReader implements both abstract methods, so it satisfies the interface. EmailReader defines .extract_email_text() instead of the required .extract_text(), so it doesn’t satisfy the contract.
Read the full article at »
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
