Python Architecture: Decoupling with Protocols and Abstract Base Classes
Overview of Structural and Nominal Typing
Modern Python development offers two primary ways to define interfaces:
Prerequisites
To follow this guide, you should possess a foundational understanding of Object-Oriented Programming (OOP) in Python. Knowledge of class inheritance, decorators, and basic type hinting is essential. Familiarity with
Key Libraries & Tools
- abc module: The standard library package containing
ABCand@abstractmethodfor creating formal interfaces. - typing module: Provides the
Protocolclass used for structural subtyping and interface definition. - Mypy: A static type checker that validates whether your classes satisfy the requirements of a specific Protocol or ABC.

Code Walkthrough: Implementing ABCs and Protocols
Defining an Abstract Base Class
In the ABC model, you define a contract that subclasses must fulfill. The relationship is rigid; the subclass must import and inherit from the parent.
from abc import ABC, abstractmethod
class Device(ABC):
@abstractmethod
def connect(self) -> None:
pass
class HueLight(Device):
def connect(self) -> None:
print("Connecting Hue Light...")
If HueLight fails to implement connect, Python prevents instantiation immediately. The error surfaces the moment you try to create the object.
Implementing Structural Protocols
Protocols remove the need for explicit inheritance. You define the interface in the module that consumes the service, following the Interface Segregation Principle.
from typing import Protocol
class DiagnosticSource(Protocol):
def status_update(self) -> str:
...
def collect_diagnostics(source: DiagnosticSource):
print(f"Log: {source.status_update()}")
Now, any object with a status_update method satisfies the DiagnosticSource requirement. The consumer defines what it needs, and the provider doesn't need to know the consumer exists. This significantly reduces coupling between modules.
Syntax Notes
- Ellipsis (...): When defining Protocols, we use the ellipsis rather than
passor a body to indicate that the method is a structural requirement with no implementation. - Decorators: ABCs require the
@abstractmethoddecorator to identify methods that must be overridden. Protocols do not require decorators for their members.
Practical Examples
In a complex Camera class has a connect() method but doesn't inherit from your Device ABC, a Protocol allows your system to treat it as a valid device without modifying the external library's source code.
Tips & Gotchas
- Error Timing: ABCs fail at instantiation. Protocols fail when the object is passed into a function that expects the protocol type. This can lead to later-stage errors if you aren't using a static type checker.
- Multiple Inheritance: Splitting a large ABC into smaller ones often leads to the complexity of multiple inheritance. Protocols make this process natural; a single class can satisfy five different protocols without inheriting from any of them.
- Shared Logic: If you need to provide default method implementations to all child classes, stick with ABCs. Protocols are purely for defining interfaces, not for sharing logic.

Fancy watching it?
Watch the full video and context