← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Structural Subtyping with Protocol

Python Python 3.8+ Intermediate
debt(d5/e3/b3/t6)
d5 Detectability Operational 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.

e3 Effort Remediation 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.

b3 Burden Structural 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.

t6 Trap Cognitive 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.

About DEBT scoring →

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)

Added 16 Mar 2026
Edited 22 Mar 2026
Views 63
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 2 pings T 0 pings F 3 pings S 1 ping S 3 pings M 0 pings T 2 pings W 3 pings T 1 ping F 2 pings S 0 pings S 0 pings M 1 ping T 0 pings W 1 ping T 1 ping F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Scrapy 10 Google 8 ChatGPT 7 Amazonbot 7 Perplexity 6 SEMrush 4 Unknown AI 3 Ahrefs 3 Bing 3 Claude 2 Meta AI 1 PetalBot 1
crawler 50 crawler_json 5
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


✓ schema.org compliant