We primarily use type hints for static type checking, but type hint information is also available at runtime! Python itself deliberately makes no use of type hints at runtime, but some external libraries do — like FastAPI and pydantic.


FastAPI uses static type hints to transform and validate API inputs at runtime.

For example, below we define two int query string parameters using type hints. Query string parameters are normally strings, but FastAPI uses the int type hints as a cue to convert them into integers:

async def get_user(skip: int = 0, limit: int = 10):
    reveal_type((skip, limit))  # tuple[int, int]
    return users[skip : skip + limit]

Under the hood, FastAPI accomplishes this using pydantic.


From the pydantic homepage:

Data validation and settings management using Python type annotations.

pydantic enforces type hints at runtime, and provides user friendly errors when data is invalid.

Define how data should be in pure, canonical Python; validate it with pydantic.

We use pydantic by defining model classes that work just like enhanced dataclasses:

from datetime import datetime
from pydantic import BaseModel

class User(BaseModel):
    id: int
    name = 'John Doe'
    signup_ts: datetime | None = None
    friends: list[int] = []

pydantic models add runtime transforms and validation based on the model's type hints. This is especially useful for processing external data, like API inputs:

external_data = {
    'id': '123',
    'signup_ts': '2019-06-01 12:22',
    'friends': [1, 2, '3'],
user = User(**external_data)

# {
#     'id': 123,
#     'signup_ts': datetime.datetime(2019, 6, 1, 12, 22),
#     'friends': [1, 2, 3],
#     'name': 'John Doe',
# }