Built-in & Collection Generics
Modern Python uses built-in collection generics (list[int]) and collections.abc abstract types to describe containers precisely - both for readers and for mypy/pyright.
Recipe
from collections.abc import Iterable, Mapping, Sequence
def total_lengths(names: Sequence[str]) -> int:
return sum(len(n) for n in names)
def merge(base: Mapping[str, int], extra: Mapping[str, int]) -> dict[str, int]:
return {**base, **extra}When to reach for this:
- Public function parameters accepting any sequence
- Read-only dict APIs without mutation
- Typing JSON-like nested structures
- Iterator-consuming utilities
Working Example
from collections.abc import Callable, Iterable, Iterator, Mapping
def index_by(items: Iterable[dict], key: str) -> dict[str, dict]:
result: dict[str, dict] = {}
for item in items:
result[str(item[key])] = item
return result
def batch(iterator: Iterator[int], size: int) -> Iterable[list[int]]:
batch_items: list[int] = []
for value in iterator:
batch_items.append(value)
if len(batch_items) == size:
yield batch_items
batch_items = []
if batch_items:
yield batch_items
def apply_all(fns: Mapping[str, Callable[[], None]]) -> None:
for fn in fns.values():
fn()
if __name__ == "__main__":
rows = [{"id": "1", "name": "Ada"}, {"id": "2", "name": "Linus"}]
print(index_by(rows, "id"))What this demonstrates:
Iterableaccepts list, generator, and custom iteratorsIteratoris single-pass - checker warns if reused incorrectly in strict modeMappingdocuments read-only dict-like without mutation methodsCallable[[], None]types zero-arg side-effect functions
Deep Dive
How It Works
- PEP 585 - Built-in generics in 3.9+ (
list[int]). - Variance - Most container generics invariant (
list[Dog]notlist[Animal]). - collections.abc - Runtime isinstance checks and structural typing targets.
- typing vs collections.abc - Prefer
collections.abcin annotations (3.9+). - Nested generics -
dict[str, list[int]]for adjacency lists.
Common ABC Choices
| Accept | When |
|---|---|
Sequence | Indexing + len needed |
Iterable | Single forward pass |
Mapping | Key lookup read-only |
MutableMapping | Needs setitem/delitem |
Python Notes
# invariant list - use Sequence for read-only param
def print_all(items: Sequence[str]) -> None:
for item in items:
print(item)Gotchas
- list vs List -
typing.Listdeprecated style on 3.9+. Fix:list[int]. - Iterable consumed twice - Type hint does not stop double iteration bug. Fix: Document or require Sequence.
- dict keys invariant -
dict[str, int]not compatible withdict[str, float]in strict variance. Fix: Use values union or Mapping. - Any in inner type -
dict[str, Any]loses safety. Fix: TypedDict or Pydantic model. - tuple variable length -
tuple[int, ...]for homogeneous variable length vs fixedtuple[int, str].
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
TypedDict | Known string keys | Dynamic keys only |
| Pydantic model | Validated nested JSON | Inner numeric kernel |
TypeAlias | Complex nested alias | Single simple list[int] |
numpy.ndarray | Numeric tensors | General Python containers |
FAQs
Sequence vs list?
Parameter type Sequence when you only read/index; list when caller must pass mutable list specifically.
Iterable vs Iterator?
Iterable produces iterator via iter(). Iterator is exhausted after one pass.
Mapping vs dict?
Mapping signals read-only API; dict when function mutates or returns concrete dict.
set generics?
set[str] for homogeneous sets - invariant like list.
frozenset typing?
frozenset[str] same as set for typing purposes.
generator return type?
Iterator[T] or Iterable[T] - Iterator stricter single-pass semantics.
bytes and memoryview?
bytes for binary; memoryview rarely in public APIs - use Buffer protocol types in advanced typing.
nested dict JSON?
dict[str, object] loose; TypedDict or models for real safety.
callable vs Callable?
collections.abc.Callable preferred in 3.9+ annotations.
read-only list?
Sequence[T] - no append in type contract though runtime list still mutable.
Related
- Type Hints Basics - intro
- TypedDict & NamedTuple - structured dicts
- Optional, Union & the | Operator - unions in containers
- Generics & TypeVar - parametric types
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.