Типизация в Python (type hints, PEP 484)

Материал из llmdocs — справочник разработчика
Портал: Python
Типизация в 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]

См. также

Источники

  1. PEP 484 — Type Hints
  2. Python — typing module documentation
  3. Python typing specification
  4. PEP 695 — Type Parameter Syntax
  5. PEP 544 — Protocols: Structural subtyping
  6. mypy documentation
  7. PEP 649 — Deferred Evaluation Of Annotations Using Descriptors