Skip to main content
A type-annotated attribute is a class, instance, or module-level variable explicitly bound to a type hint using the colon (:) syntax defined in PEP 526. These annotations provide static type metadata for static analysis tools (such as mypy or pyright) and are evaluated at runtime to populate the __annotations__ dictionary of the enclosing scope, though the Python interpreter does not enforce these types during execution.

Syntax

Runtime Mechanics

When the Python interpreter encounters a type-annotated attribute within a class body, it updates the class’s __annotations__ dunder attribute. The instantiation of the actual variable in memory depends on whether a default value is assigned:
  • Uninitialized Annotations: If no default value is provided, the attribute’s type is recorded in __annotations__, but the variable is not added to the class or instance __dict__. Attempting to access the attribute at runtime before explicitly assigning a value to it will raise an AttributeError.
  • Initialized Annotations: If a default value is provided, the attribute is added to both __annotations__ and the class __dict__.

ClassVar vs. Instance Attributes

By default, static type checkers interpret standard type-annotated attributes within a class body as instance attributes. To explicitly define an attribute as a class variable (indicating it is shared across all instances and should not be shadowed by an instance variable), the typing.ClassVar generic type is required.
At runtime, ClassVar does not alter the behavior or memory layout of the attribute; it is purely a static typing construct. The __annotations__ dictionary will store the type as typing.ClassVar[int].

Stringified Annotations (PEP 563)

If from __future__ import annotations is declared at the top of a module, Python defers the runtime evaluation of type-annotated attributes. Instead of storing the evaluated type objects (e.g., <class 'int'>), the __annotations__ dictionary stores the string representation of the type hint (e.g., 'int').
This mechanism resolves forward references and reduces module initialization time by bypassing the immediate evaluation of the type expressions. Tools requiring the actual type objects at runtime must resolve these strings using typing.get_type_hints().
Tired of Poor Python Skills? Fix That With Deep Grasping!Learn More