Skip to main content
🏷️
PEP 526 · Speedrun · Python 3.6 (2016)

PEP 526 in 90 seconds — class + module-level type annotations

Annotate fields, not just function params.

PEP 484 (2015) added type hints — but only on function parameters and return values. Class fields and module-level globals had no way to declare types except via comments: ```python class Item: name = '' # type: str quantity = 0 # type: int ``` Ugly, parser-unfriendly, easy to drift. PEP 526 (Python 3.6, 2016) added the syntax we now take for granted: ```python class Item: name: str quantity: int = 0 ```

Three patterns shipped together: - **Class attribute** with type only — `name: str` (no value; signals "every instance must have this"). - **Class attribute** with type + default — `quantity: int = 0`. - **Module-level global** — `MAX_RETRIES: int = 5` at the top of a module. The runtime stores the type in `__annotations__` but doesn't enforce it (same as PEP 484). The ecosystem leans on this: `@dataclass`, Pydantic BaseModel, SQLAlchemy 2.0 ORM, msgspec, FastAPI request bodies — all read `__annotations__` to synthesise `__init__`, validation, and OpenAPI schemas.

Before — Python ≤ 3.5, type-comment workaround
class Inventory:
    # type: comments — readable by mypy but not by the parser
    name = ""           # type: str
    quantity = 0        # type: int
    prices = None       # type: dict[str, float]

    def __init__(self, name):
        # type: (str) -> None
        self.name = name
        self.prices = {}

# Module-level type-commented global:
MAX_RETRIES = 5  # type: int
After — Python 3.6+, native annotation syntax
class Inventory:
    name: str
    quantity: int = 0
    prices: dict

    def __init__(self, name: str):
        self.name = name
        self.prices = {}

# Module-level annotation
MAX_RETRIES: int = 5

# What @dataclass / Pydantic / SQLAlchemy 2.0 all leverage:
# Inventory.__annotations__ == {
#   "name": str, "quantity": int, "prices": dict
# }
# Reading this dict is how those libs synthesise __init__, validators,
# and ORM column definitions automatically.

🎯 Predict the output

What does this print? PEP 526 annotations are stored in `__annotations__` at runtime, but don't enforce types.

class Inventory:
    name: str
    quantity: int = 0
    prices: dict

    def __init__(self, name: str):
        self.name = name
        self.prices = {}

inv = Inventory("apples")
inv.prices["red"] = 1.50
inv.prices["green"] = 1.20
print(inv.name)
print(inv.quantity)
print(inv.prices)
print(sorted(Inventory.__annotations__.keys()))
Type hints & dataclasses → Foundations track

Or speedrun another PEP

PEP 484Python stays dynamic — but you can opt into typing.
PEP 484 in 90 seconds — type hints, without TypeScript-envy
PEP 622Switch statements are a 5% feature. Pattern matching is the 95%.
PEP 622 in 90 seconds — match / case isn't just a switch
PEP 8Most of PEP 8 is decoration. Five rules carry the weight.
PEP 8 in 90 seconds — the style rules that actually matter