Python Abstractions: Mastering Protocols and Abstract Base Classes
Overview of Abstraction Layers
Defining interfaces allows developers to create a contract between different parts of a software system. In , we primarily use (ABCs) and to achieve this. These tools enable you to write code that depends on behaviors rather than specific implementations, facilitating cleaner . While both define what a class should do, they differ significantly in how they enforce these rules—one through explicit inheritance and the other through structural typing.
Prerequisites
To get the most out of this guide, you should understand fundamentals, including classes and methods. Familiarity with type hinting and basic object-oriented programming (OOP) concepts like inheritance will help you navigate the differences between nominal and structural typing.
Key Libraries & Tools
- abc: The standard library module providing the
ABCclass andabstractmethoddecorator for defining formal interfaces. - typing: Contains the
Protocolclass and theruntime_checkabledecorator used for structural typing and runtime validation. - Pylance/Mypy: Essential static analysis tools that help catch interface mismatches before you ever run your code.
Code Walkthrough: Implementing ABCs
use nominal typing. You must explicitly inherit from the base class to satisfy the interface. This is perfect when you want to provide a base implementation to avoid code duplication.
from abc import ABC, abstractmethod
class SerializedFileHandler(ABC):
def __init__(self, filename: str):
self.filename = filename
@abstractmethod
def serialize(self, data: dict) -> bytes:
"""Must be implemented by sub-classes."""
pass
def write(self, data: dict):
# Shared logic that uses the abstract method
content = self.serialize(data)
with open(self.filename, 'wb') as f:
f.write(content)
In this snippet, write is a concrete method. Any child class, like a JsonHandler, only needs to implement serialize. The ABC handles the file I/O logic, keeping your sub-classes lean.
Syntax Notes: The Power of Protocols
(introduced in ) implement "duck typing." An object satisfies a simply by having the required methods, even without inheritance.
from typing import Protocol, runtime_checkable
@runtime_checkable
class Writable(Protocol):
def write(self, data: dict) -> None: ...
The ... (ellipsis) is standard syntax for protocol methods. The @runtime_checkable decorator is vital if you intend to use isinstance() checks later; without it, will throw an error because are normally for static type checkers only.
Practical Examples
Use when working with third-party libraries. If a library returns an object with a write() method, you can define a Writable in your own code to type-hint that object. You cannot make that third-party class inherit from your , making the only viable path for strict typing.
Tips & Gotchas
Don't rely solely on runtime checks for . The interpreter often performs shallow checks. For instance, it might verify a method exists but ignore signature mismatches (like expecting bytes but receiving a dictionary). Always pair these abstractions with a static type checker like to ensure your data flows correctly through the interface.
- 42%· concepts
- 21%· languages
- 16%· concepts
- 5%· concepts
- 5%· tools
- Other topics
- 11%

Protocols vs ABCs in Python - When to Use Which One?
WatchArjanCodes // 15:31
On this channel, I post videos about programming and software design to help you take your coding skills to the next level. I'm an entrepreneur and a university lecturer in computer science, with more than 20 years of experience in software development and design. If you're a software developer and you want to improve your development skills, and learn more about programming in general, make sure to subscribe for helpful videos. I post a video here every Friday. If you have any suggestion for a topic you'd like me to cover, just leave a comment on any of my videos and I'll take it under consideration. Thanks for watching!