Generic classes
The typing lesson made functions generic. Classes can take type parameters too, so a container promises that what comes out has the type of what went in.
class Stack[T]:
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
s = Stack[int]()
s.push(3)
print(s.pop(), Stack.__type_params__)Stack[int] tells a type checker that this stack holds ints, so s.push("x") is flagged before the code runs. At runtime nothing is checked.
Bounds and the older spelling
[T: Hashable] limits T to subtypes of a type, and [T: (int, str)] to exactly those types. Code written before Python 3.12 declares type variables with TypeVar and inherits from Generic:
from typing import Generic, TypeVar
N = TypeVar("N", bound=float)
class Box(Generic[N]):
def __init__(self, item: N) -> None:
self.item = item
print(Box(2.5).item, N.__bound__)Your task
Write a generic class Queue[T] with put(item: T) -> None, get() -> T returning items first in, first out (raising IndexError when empty), and __len__. Then write Labelled in the older style: L = TypeVar("L") and class Labelled(Generic[L]) whose __init__(self, label: str, value: L) stores both.
The explanation opens here once every check passes.