← Назад к списку
ТехническаяPythonMiddle

Зачем нужны аннотации типов в Python и что такое Protocol и дженерики?

Короткий ответ

  • Аннотации не проверяются в рантайме — это подсказки
  • Статическую проверку делают mypy, pyright, ruff
  • Optional[X] — это X | None, обязательная проверка на None
  • Protocol — структурная типизация, «утиные» интерфейсы
  • Дженерики: list[int], dict[str, User], TypeVar / class C[T] (3.12+)
  • Pydantic и FastAPI используют аннотации в рантайме

Типизация в Python опциональна и статична: аннотации документируют контракты и ловят ошибки через mypy/pyright, а Protocol даёт интерфейсы без наследования.

Как сказать вслух

пример ответа

Аннотации типов сам интерпретатор не проверяет — это контракт для статических анализаторов вроде mypy и для читателя кода. Они ловят целый класс ошибок до запуска: передали None туда, где его не ждут, перепутали типы в словаре. Protocol — это структурная типизация: класс подходит под интерфейс, если у него есть нужные методы, наследоваться не нужно. А дженерики позволяют типизировать контейнеры и свои обобщённые классы.

Подробный ответ

Основной ответ

Python остаётся динамическим: аннотации (PEP 484) в рантайме почти не используются интерпретатором, но статические анализаторы (mypy, pyright) проверяют по ним согласованность кода. Современный синтаксис: int | None вместо Optional[int], list[int] вместо List[int], а с Python 3.12 — компактные дженерики def first[T](xs: list[T]) -> T и class Stack[T]. Protocol (PEP 544) описывает интерфейс структурно: любой объект с подходящими методами проходит проверку без наследования — это типизированная «утиная типизация», удобная для DI и тестовых заглушек. Отдельный пласт — рантайм-использование аннотаций: Pydantic валидирует данные по типам, FastAPI строит по ним параметры и документацию, dataclasses генерируют конструкторы.

Ключевые моменты

  • Статика, не рантайм. Неверная аннотация не уронит программу — ошибки находит анализатор в CI; поэтому типизацию внедряют постепенно.
  • Protocol против ABC. ABC требует явного наследования (номинальная типизация), Protocol сверяет структуру — меньше связность между модулями.
  • Дженерики. TypeVar или синтаксис 3.12+ связывают типы входа и выхода: функция, принимающая list[T], возвращает T, и анализатор это отслеживает.
  • Типы в рантайме. Pydantic, FastAPI, dataclasses, typer читают аннотации через механизмы интроспекции и строят на них валидацию и API.

Практический контекст

В продакшене типизация — стандарт: mypy или pyright в CI, типизированные сигнатуры в публичных API, Protocol для зависимостей в тестах. Интервьюер проверяет, отличает ли кандидат статическую проверку от рантайма, знает ли Optional-дисциплину и умеет ли типизировать обобщённый код. Частый практический вопрос — чем Protocol лучше ABC для слабой связности.

Пример кода

from typing import Protocol

class Notifier(Protocol):
    def send(self, user_id: int, text: str) -> None: ...

class EmailNotifier:
    def send(self, user_id: int, text: str) -> None:
        print(f"email to {user_id}: {text}")

def notify_all(notifier: Notifier, ids: list[int]) -> None:
    for i in ids:
        notifier.send(i, "hello")

notify_all(EmailNotifier(), [1, 2])  # наследование не требуется

Частые ошибки

  • Считают, что Python «проверит типы сам» и аннотации защищают в рантайме
  • Пишут Optional-аргументы, но не обрабатывают None по месту использования
  • Злоупотребляют Any, сводя пользу статического анализа к нулю

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru