devnotes.

Abstract Base Classes and Protocols

Python provides two ways to define interfaces more formally than plain duck typing: abstract base classes (nominal, inheritance-based) and Protocols (structural, inheritance-free).

from abc import ABC, abstractmethod

class Shape(ABC):                       # inheriting from ABC marks this as abstract
    @abstractmethod
    def area(self):                        # subclasses MUST implement this
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    def area(self):
        return 3.14159 * self.radius ** 2

# shape = Shape()      # ❌ TypeError — can't instantiate an abstract class directly
circle = Circle(5)       # ✅ fine — Circle implements the required area()

from typing import Protocol

class Drawable(Protocol):        # structural — no inheritance needed to "implement" this
    def draw(self) -> None: ...

class Square:                      # doesn't inherit from Drawable at all...
    def draw(self) -> None:
        print("Drawing a square")

def render(obj: Drawable) -> None:   # ...but still satisfies the Protocol, checked by type checkers
    obj.draw()

render(Square())   # works — Square has a matching draw() method

An abstract base class (ABC from abc) is Python's closest equivalent to a Java/C++ interface: marking a class as abstract with ABC and its methods with @abstractmethod prevents instantiating the base class directly and forces every concrete subclass to implement those methods, enforced with a real TypeError at instantiation time if they don't.

A Protocol (typing.Protocol, Python 3.8+) takes the opposite approach: structural typing, where a class satisfies the protocol simply by having the right methods with the right signatures — no explicit inheritance required at all, matching Python's duck-typing spirit while still giving type checkers (like mypy) something concrete to verify. Reach for an ABC when you want to enforce a contract at runtime with actual errors; reach for a Protocol when you want static-analysis-time checking without imposing an inheritance relationship on unrelated classes.

Abstract Base Classes and Protocols in Python | DevNotes