What is duck typing, and how does `typing.Protocol` formalize it?

5 minintermediatefundamentalstypingduck-typing

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 want mypy to 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.