20 Python Protocol vs ABC
The core insight: Protocol checks “can it do X?” while ABC checks “is it a Y?”
20.1 What is Protocol?
Protocol (from typing module) defines structural subtyping (also called “duck typing” but static). It specifies what methods/attributes a class should have, without requiring explicit inheritance.
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...Any class with a draw() method automatically satisfies this protocol—no inheritance needed.
20.2 What is ABC?
ABC (Abstract Base Class) from abc module defines nominal subtyping. Classes must explicitly inherit from the ABC to be considered a subtype.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...Classes must inherit from Shape and implement area() to be valid.
20.3 Key Differences
20.3.1 1. Inheritance Requirement
Protocol (Structural) ABC (Nominal)
Class A ──┐ Class A ──┐
│ │ inherits
Class B ──┤ all have ↓
│ method X() ABC Shape
Class C ──┘ ↑
│ inherits
↓ │
All satisfy Class B
Protocol X
(no inheritance!) (must inherit!)
Protocol: If it walks like a duck and quacks like a duck, it’s a duck ABC: You must be born a duck (inherit from Duck)
20.3.2 2. Code Example
from typing import Protocol
from abc import ABC, abstractmethod
# Protocol approach
class Flyable(Protocol):
def fly(self) -> str: ...
# ABC approach
class Animal(ABC):
@abstractmethod
def speak(self) -> str: ...
# Using Protocol - no inheritance needed
class Bird:
def fly(self) -> str:
return "Flying"
class Plane:
def fly(self) -> str:
return "Soaring"
def make_it_fly(obj: Flyable) -> None: # Both Bird and Plane work!
print(obj.fly())
# Using ABC - inheritance required
class Dog(Animal): # Must inherit
def speak(self) -> str:
return "Woof"
# This won't work as Animal subtype:
class Robot:
def speak(self) -> str:
return "Beep"
# Robot has speak(), but NOT an Animal (no inheritance)20.3.3 3. When to Use Each
Use Protocol when: - Working with third-party code you can’t modify - Want flexibility (anything matching the interface works) - Doing type checking without changing existing code - Building libraries that don’t force inheritance on users
Use ABC when: - You control the class hierarchy - Need to enforce implementation with @abstractmethod - Want shared implementation (concrete methods in base) - Building framework where inheritance makes semantic sense
20.4 Runtime Behavior
from typing import Protocol, runtime_checkable
@runtime_checkable # Must add this for isinstance()
class Renderable(Protocol):
def render(self) -> str: ...
class Button:
def render(self) -> str:
return "<button/>"
# Works with @runtime_checkable
print(isinstance(Button(), Renderable)) # True
# ABC always works with isinstance()
from abc import ABC
class Base(ABC): pass
class Derived(Base): pass
print(isinstance(Derived(), Base)) # True- Protocol: Need
@runtime_checkabledecorator forisinstance()checks - ABC:
isinstance()works automatically
20.5 Summary Table
| Aspect | Protocol | ABC |
|---|---|---|
| Subtyping | Structural (duck typing) | Nominal (inheritance) |
| Inheritance | Not required | Required |
| Flexibility | High (any matching class) | Low (must inherit) |
| Runtime check | Need @runtime_checkable |
Built-in |
| Shared code | No | Yes (concrete methods) |
| Use case | Type hints, flexibility | Frameworks, hierarchies |