We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Structural Typing and the Hard Parts step 1 of 24
Protocols: what structural subtyping actually asserts
A function that demands a nominal base class is viral. Every caller must either subclass your type or write an adapter — including callers holding a stdlib object, a third-party client, or a test double they do not own. That is how a two-line helper ends up dictating the inheritance graph of a codebase, and it is the single most common reason a Python interface is rejected in review.
typing.Protocol inverts the requirement. A class satisfies a protocol by
having compatible members; the protocol need not appear anywhere in the
class’s MRO, and the implementing class need not import it. The protocol is
a description of a shape, not a membership card.
Your task
Define a protocol SupportsSizeBytes declaring exactly one member —
size_bytes(self) -> int — and a function
def total_size(items: Iterable[SupportsSizeBytes]) -> int:
that sums it over any iterable. Then implement three completely unrelated classes, none of which mentions the protocol:
-
Blob— a frozen dataclass with annbytes: intfield; its size isnbytes. -
TextFile— a plain class holdingtext: str; its size is the length of the UTF-8 encoding, not the length of the string. (len("日本語")is 3; its size on the wire is 9. Getting this wrong is a real bug, not a contrived one.) -
Packet— aNamedTuplewithwords: int; its size is4 * words + 8.
Finally:
def solve(blob_sizes: list[int], texts: list[str], packet_words: list[int]) -> int:
builds one Blob per entry of blob_sizes, one TextFile per entry of
texts, one Packet per entry of packet_words, puts them all in one
list, and returns total_size of it.
Where the type system does the work
A heterogeneous list has no useful inferred type — mypy will land on
something like list[object], and total_size will reject it. The list must
be declared:
items: list[SupportsSizeBytes] = []
That single annotation is the whole lesson. It is the only place the three classes are asserted to share a shape, and it is checked structurally at that point, not at each class definition.
Two rules that bite later
A protocol’s other bases must all themselves be protocols. And if you
subclass a protocol while forgetting to list Protocol among the bases, you
do not get a protocol — you silently get an ordinary ABC, and every implicit
implementer stops type-checking with an error that points at the call site
rather than at your class.
Your submission must pass mypy --strict.
Loading visualization…
Stuck?
Python reference solution
Sign in to attempt this problem and reveal the reference solution.