Week 8 - The Type System: Generics, Protocols, Variance, and typing.*¶
8.1 Conceptual Core¶
- Python's type system is gradual and structural-where-it-matters.
Protocollets duck typing meet static checking - the type system catches up to the language's actual semantics. - Variance:
list[Cat]is not alist[Animal](mutable → invariant).Sequence[Cat]is aSequence[Animal](read-only → covariant).Callable[[Animal], None]acceptsCallable[[Cat], None]? No (parameters are contravariant).
8.2 Mechanical Detail¶
- Generics (PEP 695, 3.12+): the new clean syntax -
def first[T](xs: list[T]) -> T: ...andclass Box[T]: .... OldTypeVarsyntax still works. Protocol,runtime_checkable. Structural typing for theIterable,Sized,SupportsLen, etc., families.Literal,LiteralString(PEP 675, security-relevant),Final,NewType,TypeAlias(PEP 695:type Vector = list[float]).overload: multiple stubs for one implementation. Use sparingly; usually a sign of conflated responsibilities, sometimes legitimately needed (e.g.,typing.cast, JSON parser return).TypeGuard(3.10) andTypeIs(3.13): user-defined narrowing predicates.TypeIsis the strictly better one going forward - it narrows in the negative branch too.Annotated[T, metadata](PEP 593): the foundation of FastAPI/Pydantic field metadata, validators, and dependency injection.
8.3 Lab - "Make Pyright Strict"¶
- Take a 500-LOC module of your existing code. Run
pyright --strict. Resolve every error. - Add a
Protocolfor a "thing-with-an-id" and refactor a function that previously tookAny. - Use
TypeIsto narrowdict | listreturned fromjson.loadsinto safe shapes for downstream use. - Where you find yourself reaching for
cast, document why and consider whether the boundary belongs at a Pydantic model.
8.4 Idiomatic & Linter Drill¶
- Enable
ruffANN(annotation hygiene),PYI(stub files). Aim for 100% annotated public surface; private may use inference.
8.5 Production Hardening Slice¶
- Add
mypy --strictto CI. Generate Sphinx docs from docstrings. Publish to GitHub Pages. By end of week 8, the project has a public docs site.
Month-2 Exit Criteria¶
Before starting Month 3, the reader should be able to:
- Write a decorator that wraps both sync and async functions and preserves their type signatures under
pyright --strict. - Choose between
dataclass,attrs,pydantic,TypedDict, andNamedTuplefor a given use case and defend the choice. - Add
Protocols to make an old codebase amenable to dependency injection without any code change at call sites. - Articulate the validation boundary in their own architecture and where the parse-don't-validate principle is or isn't held.