Типизация в Python (type hints, PEP 484)
| Основа | PEP 484 (2014), Python 3.5 |
|---|---|
| Авторы PEP 484 | Гвидо ван Россум, Юкка Лехтосало, Лукаш Ланга |
| Синтаксис аннотаций | PEP 3107 (Python 3.0) |
| Подход | постепенная (gradual) типизация |
| Модуль | typing |
| Проверка | mypy, pyright, ty, Pyrefly |
| Проверка во время выполнения | Pydantic, beartype |
Типизация в Python — система необязательных аннотаций типов (type hints), с помощью которых программист указывает, какие типы ожидают функции и хранят переменные. Python остаётся языком с динамической типизацией: интерпретатор аннотации не проверяет и выполняет код так же, как без них. Зато статические анализаторы — mypy, pyright и другие — находят по ним ошибки до запуска, а редакторы дают точное автодополнение и навигацию по коду.
Основу системы заложил PEP 484 (2014), вошедший в Python 3.5.[1] Подход называют постепенной типизацией: аннотации можно добавлять по частям, начиная с самых важных модулей, а неаннотированный код просто не проверяется.
История
Синтаксис аннотаций функций появился ещё в Python 3.0 (PEP 3107, 2006), но без определённого смысла. Юкка Лехтосало начал разрабатывать статический анализатор mypy в рамках диссертации в 2012 году; его идеи легли в основу PEP 484, написанного вместе с Гвидо ван Россумом и Лукашем Лангой. Дальше систему развивали десятки PEP (см. PEP):
| Версия | Изменение |
|---|---|
| 3.5 | PEP 484: модуль typing, обобщённые типы, Optional, Union |
| 3.6 | PEP 526: аннотации переменных x: int = 0 |
| 3.8 | PEP 544: протоколы (структурная типизация), Literal, TypedDict, Final |
| 3.9 | PEP 585: list[int] вместо typing.List[int] |
| 3.10 | PEP 604: int | None вместо Optional[int] |
| 3.12 | PEP 695: синтаксис параметров типов def f[T](x: T) -> T и оператор type[4] |
| 3.14 | PEP 649: отложенное вычисление аннотаций[7] |
Правила, общие для всех анализаторов, собраны в спецификацию системы типов Python.[3]
Синтаксис
from collections.abc import Iterable, Callable
from typing import Literal, TypedDict, Protocol
def greet(name: str, times: int = 1) -> str:
return ", ".join([f"Привет, {name}"] * times)
def first_or_none(items: list[str]) -> str | None: # значение может отсутствовать
return items[0] if items else None
def total(prices: Iterable[float]) -> float: # принимает любой итерируемый объект
return sum(prices)
Mode = Literal["r", "w", "a"] # только эти строки
class User(TypedDict): # словарь с известными ключами
id: int
email: str
def apply[T](func: Callable[[T], T], value: T) -> T: # обобщённая функция (3.12+)
return func(value)
class SupportsClose(Protocol): # структурный тип: «что угодно с close()»
def close(self) -> None: ...
Протоколы описывают «утиную типизацию» статически: класс подходит под протокол, если у него есть нужные методы, без явного наследования.[5] Аннотировать стоит прежде всего публичные функции — параметры и возвращаемые значения; типы локальных переменных анализатор обычно выводит сам.
Проверка
uv add --dev mypy
uv run mypy src/ # статическая проверка
def double(x: int) -> int:
return x * 2
double("5") # mypy: Argument 1 to "double" has incompatible type "str"; expected "int"
Без анализатора этот вызов выполнится и вернёт "55" — ошибку найдёт только тест или пользователь. Популярные анализаторы:
- mypy — эталонная реализация, развивается вместе со стандартом;[6]
- pyright от Microsoft — быстрый, лежит в основе Pylance в VS Code;
- ty от Astral (авторов Ruff и uv) и Pyrefly от Meta — новые анализаторы на Rust, ориентированные на скорость.
Анализатор запускают в редакторе и в CI/CD, а строгость наращивают постепенно — например, включая строгий режим для новых модулей.
Типы во время выполнения
Аннотации доступны программе во время выполнения, и библиотеки используют их для поведения: dataclasses генерируют конструкторы по аннотациям полей, Pydantic проверяет и преобразует входные данные, FastAPI строит по типам параметров валидацию запросов и описание OpenAPI.
from pydantic import BaseModel
class Order(BaseModel):
id: int
amount: float
status: Literal["new", "paid"] = "new"
Order.model_validate({"id": "42", "amount": "9.5"}) # строки приведены к int и float
Практические советы
- Используйте современный синтаксис:
list[int],X | None, абстрактные типы изcollections.abcдля параметров. - Принимайте общие типы (
Iterable,Mapping), возвращайте конкретные (list,dict). - Избегайте
Any— он отключает проверку; если тип действительно неизвестен, лучшеobject. - Для сторонних библиотек без аннотаций ставьте пакеты-заглушки
types-*из репозитория typeshed. - Аннотации — ещё и документация: код с типами проще читать людям и языковым моделям.
Полный справочник — документация модуля typing.[2]