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
Abstract Base Classes
(ABCs) and
Protocols
to achieve this. These tools enable you to write code that depends on behaviors rather than specific implementations, facilitating cleaner
Dependency Inversion
. 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 ABC class and abstractmethod decorator for defining formal interfaces.
  • typing: Contains the Protocol class and the runtime_checkable decorator 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
Python 3.8
) implement "duck typing." An object satisfies a
Protocols
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
Protocols
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
Protocols
in your own code to type-hint that object. You cannot make that third-party class inherit from your
Abstract Base Classes
, making
Protocols
the only viable path for strict typing.

Tips & Gotchas

Don't rely solely on runtime checks for

. The
Python
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
Mypy
to ensure your data flows correctly through the interface.

Python Abstractions: Mastering Protocols and Abstract Base Classes

Fancy watching it?

Watch the full video and context

3 min read