Type Hints and Static Typing
Python type hints are checked by tools, not by the interpreter. Know how checkers solve generics and variance, how narrowing works, and where runtime libraries such as Pydantic step in.
Key points
- 1
Annotations are stored in
__annotations__but never enforced by CPython; mypy, pyright or a runtime validator does the checking. - 2
In 3.13 function annotations are evaluated when the
defruns;from __future__ import annotationsstores strings, and 3.14 (PEP 649) evaluates lazily by default. - 3
Mutable containers are invariant (
list,dict); read-only protocols such asSequenceandMappingare covariant; callables are contravariant in their parameters. - 4
PEP 695 (3.12) adds
def f[T],class Box[T]andtype Alias = ..., with variance inferred from howTis used. - 5
TypeIsnarrows both branches and requires a subtype;TypeGuardnarrows only the true branch and allows unrelated types. - 6
Type decorators with
ParamSpec(andConcatenatewhen they add or remove arguments) so the wrapped signature is preserved. - 7
Pydantic v2 validates at runtime: lax mode coerces values like "1" to 1, strict mode refuses them, and
model_constructskips validation.
Common traps
A decorator typed
Callable[..., Any]silently turns every decorated function intodef (*Any, **Any) -> Any.runtime_checkableisinstance checks only that attributes exist, not their types or signatures.# type: ignore[code]with the wrong code hides nothing; name the exact error code mypy prints.
Test yourself on Type Hints and Static Typing
Ten questions, with the answer and explanation after each one.