What is duck typing, and how does `typing.Protocol` formalize it?
Quick Answer
Duck typing means Python checks **behavior, not declared type** — "if it walks like a duck and quacks like a duck, it's a duck." Any object with the right methods/attributes works, regardless of its class hierarchy. `typing.Protocol` (PEP 544) lets you describe that *structurally* for static type checkers — a class satisfies a `Protocol` by having matching methods, with no explicit inheritance required.
Detailed Answer
Duck typing in practice
class Duck:
def quack(self):
return "Quack!"
class Person:
def quack(self):
return "I'm quacking!"
def make_it_quack(thing):
return thing.quack() # no type check — just calls the method
make_it_quack(Duck()) # works
make_it_quack(Person()) # also works — Person isn't a Duck subclass
make_it_quack never checks isinstance(thing, Duck) — it just calls
.quack() and trusts that the object supports it. This is why
for x in anything: works on lists, files, generators, and custom
classes alike: they all implement __iter__, regardless of ancestry.
The problem for static typing
Duck typing is dynamic — you find out at runtime whether an object has the
right method. Static type checkers (mypy, pyright) need something they
can verify ahead of time without requiring every duck-typed class to
inherit from a common base (which would defeat the point).
Protocol: structural typing for static checkers
from typing import Protocol
class Quacks(Protocol):
def quack(self) -> str: ...
def make_it_quack(thing: Quacks) -> str:
return thing.quack()
make_it_quack(Duck()) # type-checks fine — Duck has a matching quack()
make_it_quack(Person()) # also fine — no inheritance from Quacks needed
Quacks is never inherited from; mypy checks whether Duck and Person
structurally match its method signatures. This is "nominal vs
structural" typing: ABC/inheritance is nominal (you must declare the
relationship), Protocol is structural (the shape is enough).
When to use which
- Plain duck typing: quick scripts, internal code, when you don't need static verification.
Protocol: public APIs and larger codebases where you wantmypyto catch "this object doesn't have the method you're calling" before runtime, without forcing every caller into a shared base class.
Interview-ready summary: Duck typing lets Python code work with any
object that has the right methods, independent of its class hierarchy.
Protocol gives that the same runtime flexibility plus static
verification — a class satisfies a Protocol by shape, not by declared
inheritance.