Protocols & ABCs
A track of P28 · Objects & Data Modelling.
Structural versus nominal typing: protocols, ABCs and when each is the right contract for an interface.
Your function needs "anything that can .read()" — a file, a socket, a
StringIO, the five-line stub your test suite fakes it with. Must every one
of those inherit from your base class, or is walking like a duck enough?
Python's honest answer is that both regimes exist, both are first-class, and
choosing between them is a genuine design decision — not a style preference.
This track is about making that choice deliberately, with the type checker on
your side either way.
The structural regime is typing.Protocol: conformance by shape. Any object
with the right methods satisfies the contract — no inheritance, no
registration, no cooperation from the class's author — which is exactly what
you need when the conforming types belong to a third-party library or to code
you cannot touch. The nominal regime is abc.ABC: conformance by declared
lineage, enforced at instantiation and testable with isinstance, which is
exactly what you want when you own the hierarchy and intend to share real
implementation between its members. The first module builds both around the
same duck and makes the trade-offs concrete; the tools return in P31 (Typing
& Robust Code), where they meet mypy at full strength.
The closing module extends contracts from behaviour to values. An enum
replaces a scatter of magic strings with a small, closed set of named members
— impossible to mistype, exhaustively checkable. A validated model applies
"parse, don't just store": data is checked and normalised once, at the
boundary, so the rest of the program only ever sees valid values — the idea
behind libraries like pydantic, built here by hand so you know what they do.
Data Modelling built objects whose states are legal; this track finishes the
pillar by writing down, in checkable form, what legal means.