d5DetectabilityOperational debt — how invisible misuse is to your safety net
Closest to 'specialist tool catches' (d5), mypy/pyright detect Protocol mismatches and missing methods at type-check time, but without these tools structural mismatches are silent.
e3EffortRemediation debt — work required to fix once spotted
Closest to 'simple parameterised fix' (e3), replacing an ABC with a Protocol or adding @runtime_checkable is a localized change in the type definition, though implementing classes don't need changes.
b3BurdenStructural debt — long-term weight of choosing wrong
Closest to 'localised tax' (b3), Protocols are typically defined in one module and used by consumers; they don't pervade the architecture but do shape API contracts for that component.
t6TrapCognitive debt — how counter-intuitive correct behaviour is
Closest to 'serious trap' (t7), the misconception that Protocol requires explicit registration like ABC contradicts how interfaces work in most other languages (Java/PHP/C#), and the runtime vs. type-check distinction with @runtime_checkable is a notable additional gotcha.
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)
🧱FUNDAMENTALS— new to this? Start with the ground floor.
PythongeneralPython is a programming language known for readable syntax and versatility, used for web development, data science, automation, and more.
Python's gentle learning curve makes it an ideal first language, while its vast ecosystem keeps it relevant for machine learning, APIs, and DevOps. Skills transfer directly to professional environments because Python runs in production at companies of every size.
💡 When Python throws IndentationError, check that every block uses the same whitespace style—pick spaces (preferably 4) and stick with them everywhere.
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