Two audiences for a single annotation
Python is dynamically typed. x: int = "five" runs without complaint and x holds the string. So why annotate?
Two reasons, and they live in different layers of the toolchain:
- Static checkers (mypy, pyright, Pylance) read annotations from source and prove invariants before code runs. A misuse fails the build, not production.
- Runtime libraries read annotations through
typing.get_type_hints()and use them to drive behavior. Pydantic compiles a validator from your model. FastAPI builds the dependency graph and OpenAPI schema. SQLAlchemy 2.0 mapsMapped[int]to a column.attrsgenerates__init__.
The same annotation has two audiences. A list[User] tells the checker what indexing returns; it tells Pydantic to deserialize each element as a User and reject anything else. In a backend codebase, knowing which layer reads which annotation is the difference between types as docs and types as the spec the framework executes.
