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.
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.