Unit 3.7 · about 3 h
16 / 324Type hints, generics, protocols and narrowing
Typed boundaries are the foundation of the next section. Hints are documentation the machine checks.
- explain
- implement
You can now
- Write PEP 695 generics and type aliases, and explain deferred annotation evaluation on Python 3.14.
- Define a
Protocoland contrast structural with nominal subtyping, includingruntime_checkablelimits. - Narrow unions with
isinstanceandassert_type, and pass mypy clean on a typed module.
Covers
- Type hints, unions, generics, protocols and narrowing
Study, in this order
6 steps
- Type parameter listsPython language reference
Sections 8.10 to 8.10.3: generic functions, classes and aliases since Python 3.12 by PEP 695; bounds, constraints and defaults
- Annotation scopes and lazy evaluationPython language reference
Sections 4.2.3 and 4.2.4: annotation scopes; lazy evaluation since 3.12, extended to all annotations in 3.14 by PEP 649 and PEP 749
- Protocolstyping - type hints and protocols
Protocols: structural subtyping;
runtime_checkablelimits (isinstancechecks presence only) - Genericstyping - type hints and protocols
Generics:
TypeVar-based generics for reading older code; write new code with the PEP 695 syntax above - Checked narrowingtyping - type hints and protocols
assert_type: ask the checker to prove a narrowed type at a program point - mypy Type CheckerAlgoMaster Python - structured revision
The mypy Type Checker lesson and its exercise; verify every syntax claim against the 3.14 sections above, since older examples may predate PEP 695
Focus on
- Annotations are lazily evaluated since 3.14 by PEP 649:
def f(x: 1/0)defines fine and fails only when__annotations__is read.get_type_hintsevaluates them in the right scope. - PEP 695 in 3.12 puts type parameters in brackets:
def first[T](xs: list[T]) -> T. Read oldTypeVarcode; write the new syntax. - A
Protocoldefines structure, not lineage.isinstanceagainst a non-runtime_checkableProtocolraisesTypeError; aruntime_checkablecheck tests member presence only. - Narrowing proves a type at a point:
isinstancebranches plusassert_typeas the checkable claim.isinstancewith a parameterized generic raisesTypeError.
Reproduce closed-book
first() plus a small Protocol and a narrowing function over int | str; run mypy 2.4.0 clean. Show that def f(x: 1/0) defines without error on 3.14 and raises only on annotation access.Traps
- Checking with
isinstance(x, list[int]). Parameterized generics are not validisinstancetargets. - Treating a
runtime_checkableProtocolas a full interface check. It verifies member presence, never signatures or semantics. - Assuming annotations run at
deftime. On 3.14 aZeroDivisionErrorinside an annotation waits until someone reads it, which delays import-time failures.
Practice
AlgoMaster Python: Exercise: mypy Type Checker
Exercises 1 to 4: exposing annotations, a NewType call, counting error codes and a pyproject.toml mypy section
Run the same checks locally with mypy 2.4.0 from the section interpreter.
Self-test
-
def f(x: 1/0) -> None: passruns without error on 3.14. When does it fail, and which PEP changed this?It fails when__annotations__is read, through the lazy__annotate__hook. PEP 649 made annotations lazily evaluated in 3.14. -
What does
isinstance([], list[int])do, and what should you write instead?It raisesTypeError: parameterized generics are not validisinstancetargets. Checkisinstance(x, list)and narrow items separately, or useassert_typefor the static claim. -
class P(Protocol): def m(self) -> int. What doesisinstance(obj, P)do without@runtime_checkable?It raisesTypeError. Structural checks need@runtime_checkable, which then checks member presence only. -
Rewrite
class Box(Generic[T])with 3.12 syntax and state the minimum version.class Box[T]: ...needs Python 3.12 or later by PEP 695.
Completion criterion
Stop when
Your typed module passes mypy clean, your Protocol accepts an unrelated matching class, and you can state exactly when each annotation evaluates.
Leads to
- 4.1 Typed interfaces, runtime validation and configuration Protocols and narrowing become service contracts.
- 3.3 Attribute lookup, object protocols and dataclasses Combine dataclasses with Protocols and precise field types.
Comes after
- 3.3 Attribute lookup, object protocols and dataclassesAdd static types and Protocols to these records.
Resources for this unit
- Python language reference Check Data model, Execution model, Import system and Exceptions whenever a claim depends on Python semantics. These pages are the authority when the course and a runtime observation disagree.
- AlgoMaster Python - structured revision Use Functions, Modules and Packages, OOP, Advanced OOP, Exception Handling, Iterators and Generators, Type Hints and Annotations only after the cookbook pass. Open it for recall practice or a diagnosed weak mechanism, not as a second complete course.
- typing - type hints and protocols Read Protocols for structural typing and
runtime_checkablelimits, Generics forTypeVargenerics, andassert_typefor narrowing checks. Use for theProtocoland narrowing parts of type-hints-protocols; PEP 695 syntax itself is in the language reference.
Also in this section
- Stas' Python Cookbook - selected path Run Part I on dictionaries, iteration, functions, closures, decorators, classes and context managers; Part II on modules, files, paths, environment and arguments; and Part IV on versions, dependencies, packaging and code quality. Finish by building one src-layout package from a blank directory.
- Packaging Python Projects Follow the packaging tutorial once from a blank directory, using a src layout, pyproject.toml, an isolated build and a clean-environment installation test.
- dataclasses - data classes Read the
@dataclassparameters (eq,order,frozen,slots),field()withdefault_factory, frozen instances, post-init processing,replace,asdictandastuple. Use for the validated frozen dataclass in attributes-protocols-dataclasses. - gc - cycle garbage collector Read the cycle collector interface:
collect,get_count,garbageanddisable/enable. Use for the cycle-collection and measurement parts of refcounting-gc-weakrefs. - weakref - weak references Read weak-reference objects (
refcallbacks,proxyobjects) and theWeakValueDictionaryexample. Use for the weak-reference part of refcounting-gc-weakrefs.