Skip to content

← Interfaces with Protocols and ABCs step 2 of 26

Easy Primitives

Write a Protocol for Three Unrelated Classes

A storage report adds up the size of every blob, text file and network packet that a user keeps. The three classes have nothing in common except a size_bytes() method. Write a protocol for that method, and a function that totals any mix of the three.

total_size([Blob(1), TextFile("ab"), Packet(2)])  # 1 + 2 + 16 = 19

What to write

SupportsSizeBytes is a Protocol with one method:

def size_bytes(self) -> int: ...

Each class gets its own size_bytes():

Class Stored value size_bytes() returns
Blob nbytes: int nbytes
TextFile text: str the number of bytes in text.encode("utf-8")
Packet words: int 4 * words + 8, four bytes per word plus an eight-byte header

Keep Blob a frozen dataclass, TextFile a plain class and Packet a NamedTuple. None of them inherits from SupportsSizeBytes. mypy matches each one by its size_bytes method.

total_size(items: Iterable[SupportsSizeBytes]) -> int adds up size_bytes() over items. Call the method on each item, without checking which class it is. Accept any iterable, including a generator, which you can read only once.

solve(blob_sizes, texts, packet_words) makes one Blob per entry in blob_sizes, one TextFile per entry in texts and one Packet per entry in packet_words. Put them all in one list, and return total_size(items). Start the list with this annotation:

items: list[SupportsSizeBytes] = []

The annotation tells mypy what the list holds, so it checks each object you add against the protocol. Without it, mypy reports Need type annotation for "items". A list written out as [Blob(1), TextFile("a")] gets the type list[object], which total_size rejects.

Edge cases

  • Text sizes count bytes, not characters. "héllo" is 6 bytes, and "日本語" is 9.
  • A packet with zero words is still 8 bytes.
  • An empty text is 0 bytes, and three empty lists total 0.
  • Count repeated entries separately, and leave the input lists unchanged.