Structural Subtyping with Protocol
Also Known As
Protocol
structural subtyping
duck typing Python
typing.Protocol
runtime_checkable
TL;DR
Protocol classes define structural interfaces — any class with matching methods satisfies the protocol without explicit inheritance (type-safe duck typing).
Explanation
Protocol (typing.Protocol, Python 3.8+): a class satisfies a Protocol if it has all required attributes and methods, regardless of inheritance. Unlike ABC (which requires explicit subclassing), Protocol works at type-check time without modifying existing classes. @runtime_checkable enables isinstance() checks at runtime. Use for: typing third-party classes you cannot modify, documenting expected interfaces without coupling to a class hierarchy, and expressing duck types with static type safety.
Common Misconception
✗ Protocol requires explicit registration like ABC — Protocol uses structural subtyping; any class with matching methods satisfies it at type-check time with no changes to the implementing class.
Why It Matters
Without Protocol, a function accepting any object with a read() method must use Any (loses type safety) or a custom ABC (requires modifying all implementing classes). Protocol gives type safety without coupling.
Common Mistakes
- Using Protocol when ABC is more appropriate — use ABC when you want enforcement at class definition time
- Not adding @runtime_checkable when isinstance() checks are needed
- Protocol with mutable attributes — structural subtyping with mutable attributes can be surprising
- Forgetting that satisfying the Protocol is checked at type-check time not runtime (without @runtime_checkable)
Code Examples
✗ Vulnerable
# Any type — loses all type safety:
def process(reader: Any) -> str:
return reader.read() # No type checking — any attribute access allowed
# ABC — requires modifying existing classes:
from abc import ABC, abstractmethod
class Readable(ABC):
@abstractmethod
def read(self) -> str: ...
# All implementing classes must inherit Readable — invasive coupling
✓ Fixed
from typing import Protocol, runtime_checkable
@runtime_checkable
class Readable(Protocol):
def read(self) -> str: ...
def process(reader: Readable) -> str:
return reader.read() # Type-checked — must have read() -> str
# Any class with read() -> str satisfies it — no inheritance needed:
class FileReader:
def read(self) -> str: return open(self.path).read()
class MockReader:
def read(self) -> str: return 'mock data'
process(FileReader()) # Type-safe
process(MockReader()) # Type-safe
isinstance(MockReader(), Readable) # True (runtime_checkable)
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
16 Mar 2026
Edited
22 Mar 2026
Views
27
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
Amazonbot 6
Perplexity 5
ChatGPT 3
Unknown AI 3
Google 3
Ahrefs 1
Also referenced
How they use it
crawler 20
crawler_json 1
Related categories
⚡
DEV INTEL
Tools & Severity
🟡 Medium
⚙ Fix effort: Medium
⚡ Quick Fix
Use Protocol to define structural interfaces in Python — any class with the required methods satisfies the Protocol without explicit inheritance, enabling PHP-style interface checking with Python's duck typing
📦 Applies To
python 3.8
web
cli
🔗 Prerequisites
🔍 Detection Hints
ABC abstract base class with all abstract methods when Protocol would be simpler; type: ignore on duck-typed arguments
Auto-detectable:
✓ Yes
mypy
pyright
pylance
⚠ Related Problems
🤖 AI Agent
Confidence: Low
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: Class