What typing.Protocol Is and How It Differs
Sveip for å vise menyen
Python 3.8 introduced typing.Protocol as a way to express structural typing – defining an interface based on what methods an object has, without requiring inheritance. Where ABCs use nominal typing ("this class is a subclass of X"), Protocols use structural typing ("this class has the right methods").
Nominal vs Structural Typing
Nominal typing (ABCs): a class must explicitly inherit from or register with the ABC to be considered compatible.
Structural typing (Protocols): a class is compatible if it has the required methods, regardless of its inheritance chain.
12345678910111213141516171819202122232425262728293031from abc import ABC, abstractmethod from typing import Protocol # Nominal: must inherit from Serializable class Serializable(ABC): @abstractmethod def serialize(self): pass # Structural: any class with serialize() qualifies class SerializableProtocol(Protocol): def serialize(self) -> str: ... # A class that has serialize() but does not inherit from anything class TradeRecord: def serialize(self): return '{"id": "TX-001", "amount": 4500.0}' # ABC – fails without inheritance or register() print(isinstance(TradeRecord(), Serializable)) # False # Protocol – passes because the method exists from typing import runtime_checkable @runtime_checkable class SerializableProtocol(Protocol): def serialize(self) -> str: ... print(isinstance(TradeRecord(), SerializableProtocol)) # True
Defining a Protocol
A Protocol class inherits from typing.Protocol. Methods are defined with ... (ellipsis) as the body – they describe the interface, not an implementation:
123456789101112131415161718192021222324252627282930313233from typing import Protocol # Protocol defining the interface for a data exporter class Exporter(Protocol): def export(self, data: list) -> str: ... def get_format(self) -> str: ... # Any class with these methods satisfies the protocol class CsvExporter: def export(self, data): return ",".join(str(item) for item in data) def get_format(self): return "CSV" class JsonExporter: def export(self, data): import json return json.dumps(data) def get_format(self): return "JSON" # Both satisfy the protocol without inheriting from it def run_export(exporter: Exporter, data: list) -> None: print(f"Format: {exporter.get_format()}") print(exporter.export(data)) run_export(CsvExporter(), ["TX-001", "TX-002"]) run_export(JsonExporter(), ["TX-001", "TX-002"])
@runtime_checkable
By default, Protocol membership cannot be checked at runtime with isinstance(). Decorating with @runtime_checkable enables it – but the check only verifies method presence, not signatures:
12345678910111213141516from typing import Protocol, runtime_checkable @runtime_checkable class Runnable(Protocol): def run(self) -> None: ... class BackgroundJob: def run(self): print("Running background job") class BrokenJob: pass print(isinstance(BackgroundJob(), Runnable)) # True – has run() print(isinstance(BrokenJob(), Runnable)) # False – missing run()
ABCs vs Protocols at a Glance
Takk for tilbakemeldingene dine!
Spør AI
Spør AI
Spør om hva du vil, eller prøv ett av de foreslåtte spørsmålene for å starte chatten vår