Python Protocol: Structural Typing with runtime_checkable
A Python Protocol describes the methods an object must provide. Structural typing checks those methods without requiring a shared parent class.
Read Classes and objects and Type hints first.
Define a protocol
Inherit from Protocol and declare the required method signatures. ... stands in for the implementation.
from typing import Protocol
class Named(Protocol):
def label(self) -> str:
...
def greet(item: Named) -> str:
return f"Hello, {item.label()}"
A type checker expects arguments to greet() to have a compatible label() method.
Use a class without inheriting the protocol
A class matches through its methods. It does not need to inherit from Named.
from typing import Protocol
class Named(Protocol):
def label(self) -> str:
...
class Player:
def label(self) -> str:
return "Ada"
def greet(item: Named) -> str:
return f"Hello, {item.label()}"
print(greet(Player()))
This prints Hello, Ada. Type checkers compare the method's parameters and return type too.
Check member presence with runtime_checkable
Add @runtime_checkable to allow isinstance() checks against a protocol.
from typing import Protocol, runtime_checkable
@runtime_checkable
class Named(Protocol):
def label(self) -> str:
...
class Player:
def label(self) -> str:
return "Ada"
print(isinstance(Player(), Named))
print(isinstance(42, Named))
This prints True and False.
Note: Runtime checks inspect member presence, not parameter or return types.
They do not replace a type checker. Without @runtime_checkable, this isinstance() check raises TypeError.
See also: Type hints, Inheritance.
Your turn
Define a Bell class with a sound(self) -> str method returning "ding".
from typing import Protocol
class Audible(Protocol):
def sound(self) -> str:
...
def announce(item: Audible) -> None:
print(item.sound())
# add three lines: define Bell, define its sound method, then return "ding"
announce(Bell())
Expected output
ding