Mastering Python Interfaces: Protocol vs. Abstract Base Classes

Overview

Software design often requires a contract between different parts of a system. In Python, we primarily use

(ABCs) and
Protocols
to define these contracts. While they appear similar, they represent two fundamentally different philosophies: nominal typing and structural typing. This guide explores how to implement both and why choosing the right one can dramatically reduce coupling in your applications.

Prerequisites

To follow this guide, you should understand:

  • Intermediate
    Python
    (Classes and Type Hinting).
  • Basic software design principles like inheritance.
  • Standard library usage (specifically the abc and typing modules).

Key Libraries & Tools

  • abc: The built-in module for defining Abstract Base Classes.
  • typing: Provides the Protocol class for structural typing.
  • Mypy
    : A static type checker to verify implementation adherence (recommended).
Mastering Python Interfaces: Protocol vs. Abstract Base Classes
Protocol Or ABC In Python - When to Use Which One?

Code Walkthrough

Implementation with Abstract Base Classes

use nominal typing. You must explicitly inherit from the base class for the type system to recognize the relationship.

from abc import ABC, abstractmethod

class Device(ABC):
    @abstractmethod
    def connect(self) -> None:
        ...

class HueLight(Device):
    def connect(self) -> None:
        print("Connecting to Hue Light")

Here, HueLight is a Device because we said so in the class definition. If HueLight fails to implement connect, Python prevents instantiation immediately.

Implementation with Protocols

use structural typing (duck typing). Relationship is determined by the presence of methods, not inheritance.

from typing import Protocol

class Device(Protocol):
    def connect(self) -> None: ...

class SmartSpeaker:
    def connect(self) -> None:
        print("Connecting to Speaker")

SmartSpeaker does not inherit from Device. However, because it has a connect method, it satisfies the

. The type checker validates this at the point of usage, such as when passing the speaker into a function expecting a Device.

Syntax Notes

  • ABC Decorators: Use @abstractmethod to mark methods that subclasses must override.
  • Ellipsis: In
    Protocols
    , use ... instead of a function body to indicate an interface definition.
  • Inheritance:
    Abstract Base Classes
    require (BaseClass), while
    Protocols
    generally do not require implementation classes to inherit from them.

Practical Examples: Interface Segregation

excel at
Interface Segregation
. Instead of one giant Device interface, you can split requirements based on who uses them.

class DiagnosticSource(Protocol):
    def status_update(self) -> str: ...

class ConnectionManager(Protocol):
    def connect(self) -> None: ...

A diagnostic tool only needs to know about DiagnosticSource. It doesn't care if the object can connect or disconnect. This limits the knowledge each module needs, making the system easier to maintain.

Tips & Gotchas

  • Failing Early:
    Abstract Base Classes
    catch errors at instantiation.
    Protocols
    catch them when the object is used or through static analysis.
  • Third-Party Code:
    Protocols
    are perfect for third-party libraries. You can define a protocol for a library class you didn't write without modifying its source code.
  • Code Reuse: If you need to share common logic (not just an interface) between subclasses,
    Abstract Base Classes
    are superior because they support standard inheritance and method implementation.
3 min read