Аннотации не проверяются при выполнении
def total(prices: list[float], discount: float = 0) -> float:
return sum(prices) * (1 - discount)
total("abc") # интерпретатор не возразит до ошибки внутри
Аннотации — подсказки для людей и инструментов. Их проверяют статические анализаторы — mypy и pyright, — а IDE берут из них автодополнение. Во время выполнения аннотации читают только те, кто делает это явно: Pydantic, FastAPI, dataclasses.
Как аннотировать
- список, словарь, множество —
list[int],dict[str, float],set[str]; - кортеж фиксированной и любой длины —
tuple[int, str],tuple[int, ...]; - может быть None —
int | None(раньшеOptional[int]); - одно из —
int | str; - функция —
Callable[[int], str]; - что угодно —
objectбезопасно,Anyотключает проверку.
Для параметров удобнее абстрактные типы из collections.abc: Iterable, Sequence, Mapping — так функция принимает и список, и кортеж, и генератор.
Optional означает «может быть None», а не «необязательный аргумент». Перед использованием значения типа X | None его проверяют на None — mypy на это и укажет.
Обобщённые типы
TypeVar связывает типы входа и выхода:
def first[T](items: list[T]) -> T: # синтаксис Python 3.12
return items[0]
first([1, 2]) # mypy знает: int
first(["a", "b"]) # str
До 3.12 то же пишут через T = TypeVar("T") и Generic[T] для классов. Ограничения — bound= или перечисление допустимых типов.
Protocol — утиная типизация для mypy
from typing import Protocol
class Closable(Protocol):
def close(self) -> None: ...
def shutdown(items: list[Closable]) -> None:
for item in items:
item.close()
Подходит любой класс с методом close, без наследования. ABC, наоборот, требует явного наследования и проверяет реализацию при создании объекта.
mypy в проекте
mypy и pyright ставят как зависимость разработки, настраивают в pyproject.toml и запускают в CI:
[tool.mypy]
python_version = "3.12"
strict = true
Код без аннотаций mypy по умолчанию почти не проверяет, поэтому строгость наращивают постепенно — сначала новые модули. Для библиотек без типов ставят пакеты-заглушки (types-requests). Точечное отключение — # type: ignore[код] с понятной причиной.
Pydantic — проверка данных на входе
Pydantic проверяет и преобразует данные во время выполнения по аннотациям модели:
from pydantic import BaseModel, Field
class OrderIn(BaseModel):
product_id: int
count: int = Field(ge=1, le=100)
comment: str | None = None
OrderIn.model_validate({"product_id": "42", "count": 2}) # "42" → 42
OrderIn.model_validate({"product_id": "abc", "count": 0}) # ValidationError
ValidationError указывает поле и причину. Обратно — model_dump() и model_dump_json(). Pydantic используют для запросов API, конфигурации, ответов внешних сервисов и LLM; FastAPI построен на нём.
Что запомнить
- Аннотации проверяет mypy, а не интерпретатор.
X | Noneнужно проверить на None перед использованием.- Для параметров —
IterableиSequence, для результата — конкретный тип. - mypy — в CI, строгость — постепенно.
- Внешние данные — через Pydantic.