Interview frequency: …
Short answer
A Protocol describes the required behavior (methods and attributes) and accepts any class that matches it structurally without explicit inheritance; it is a static form of duck typing.
Explanation
With an ABC a class must inherit explicitly, whereas with a Protocol it is enough to have the required methods. This is useful for decoupling layers: the consuming layer defines the interface it needs, and any implementation matches it without knowing about it. Keep the protocol small (one or two methods). The check is static only, unless you decorate it with runtime_checkable to use isinstance, and even then it only checks that the names exist, not the signatures.
Code example
from typing import Protocol
class Notifier(Protocol):
def send(self, to: str, body: str) -> None: ...
class SmsNotifier: # Do not inherit Notifier
def send(self, to: str, body: str) -> None:
...
def notify(n: Notifier, user: str) -> None:
n.send(user, "Welcome")
notify(SmsNotifier(), "+2010") # Structurally compatibleCommon mistakes
- Defining a huge Protocol that ties the consumer to the details of one implementation.
- Relying on isinstance with runtime_checkable to infer that signatures match.