Аннотации типов в Python: польза без проверок в рантайме
Аннотации не влияют на выполнение кода — интерпретатор их игнорирует. Разбираем, что они дают на самом деле: подсказки в IDE, документацию и работу mypy, и где начинают лгать.

Строка def send(user: User, retries: int = 3) -> bool: при запуске ведёт себя ровно так же, как без аннотаций: CPython не проверяет, что user действительно User, а retries — целое число. Можно передать список вместо User, и программа не упадёт из-за аннотации — упадёт позже, когда обратится к несуществующему атрибуту. Аннотации в Python по умолчанию не проверяют ничего. И при этом они окупаются — в трёх местах, о которых ниже.
Разбор для тех, кто уже пишет на Python и колеблется, стоит ли расставлять типы в проекте без обязательной типизации. Речь про CPython 3.10–3.13 и mypy как отдельный инструмент. Синтаксис list[int] без импорта из typing работает с Python 3.9.
Что аннотации не делают
Интерпретатор при выполнении функции аннотации не читает. Они собираются в словарь __annotations__ у функции или класса и лежат там как данные. Проверить это можно вручную:
# Python 3.11
def send(user_id: int, retries: int = 3) -> bool:
return retries > 0
print(send.__annotations__)
# {'user_id': <class 'int'>, 'retries': <class 'int'>, 'return': <class 'bool'>}
# аннотация не мешает передать строку
print(send("это точно не int", retries="тоже не int"))
# True — упадёт только там, где строка используется как число
Вывод простой: аннотация — это метаданные, а не контракт. Всё, что она даёт, дают инструменты, которые эти метаданные читают, — а не сам Python.
Три места, где они окупаются
1. Статический анализ до запуска
Главный потребитель аннотаций — mypy1. Он читает типы и находит ошибки, которые иначе всплыли бы в проде. Установка и запуск:
# mypy 1.x
pip install mypy
mypy myproject/
Пример, где mypy ловит ошибку, а интерпретатор — нет:
# Python 3.11, проверяется mypy 1.x
def discount(price: float, percent: int) -> float:
return price * (1 - percent / 100)
# опечатка: аргументы перепутаны местами при вызове
total: float = discount(percent=1990, price=15)
# в рантайме это отработает и вернёт мусорное число
# mypy молчит здесь, потому что оба int/float совместимы —
# но поймает, если передать несовместимый тип:
name: str = discount(15.0, 20)
# error: Incompatible types in assignment
# (expression has type "float", variable has type "str")
Ценность тут в том, что ошибка находится в CI или в редакторе, а не в 3 часа ночи по логам. Но за это платят: mypy добавляет шаг в пайплайн, требует настройки строгости и на большом нетипизированном проекте сначала выдаёт сотни предупреждений.
2. Автодополнение и навигация в редакторе
PyCharm, VS Code с Pylance и любой LSP на базе pyright используют аннотации, чтобы предлагать методы и подсвечивать неверные вызовы прямо при наборе. Без типа возвращаемого значения редактор не знает, что вернула функция, и не подскажет методы результата. С аннотацией -> list[User] он сразу предложит и .append, и атрибуты User у элементов.
Это работает и на чужом коде: открывая библиотеку с аннотациями, вы видите ожидаемые типы аргументов без похода в документацию.
3. Документация, которая не устаревает молча
Сигнатура def parse(raw: bytes, encoding: str = "utf-8") -> dict[str, str] говорит о функции больше, чем абзац докстроки, и не расходится с кодом так же незаметно, как комментарий. Изменили возвращаемый тип — аннотация в сигнатуре меняется рядом, а не остаётся врать в комментарии двумя экранами ниже.
Аннотация — это тест, который выполняется без запуска программы. Она стоит ровно столько, сколько стоит инструмент, который её читает: без mypy или редактора с LSP это просто украшение сигнатуры.
Где аннотации начинают лгать
Раз интерпретатор их не проверяет, аннотация легко расходится с реальностью — и тогда она хуже её отсутствия, потому что вводит в заблуждение.
- Данные из внешнего мира. Аннотировали
config: dict[str, int], но значения пришли из JSON и оказались строками.mypyверит вашей аннотации на входе и проблему не увидит — падение будет в рантайме. Anyзаражает всё вокруг. Стоит одному значению статьAny, иmypyперестаёт проверять цепочку операций с ним. Импорт нетипизированной библиотеки часто протаскиваетAnyв ваш код молча.- Приведения
cast.typing.cast(User, obj)ничего не делает в рантайме — это обещание анализатору. Ошиблись в обещании — анализатор поверит и пропустит ошибку. - Отложенное вычисление. С
from __future__ import annotationsаннотации хранятся как строки и не вычисляются при импорте. Код, который читает__annotations__в рантайме (Pydantic, dataclasses в отдельных сценариях), получит строки вместо типов и может повести себя иначе.
Когда типизацию проверяют в рантайме
Отдельная история — библиотеки, которые читают те же аннотации и проверяют данные уже при выполнении. Здесь аннотация перестаёт быть только метаданными:
- Pydantic v2 валидирует и приводит данные по аннотациям модели: пришла строка
"3"в полеint— приведёт к3или броситValidationError, в зависимости от режима. - FastAPI использует аннотации хендлеров, чтобы разобрать тело запроса и вернуть 422 при несоответствии.
- dataclasses читают аннотации, чтобы сгенерировать
__init__, но сами типы не проверяют — это частая ловушка:@dataclassпримет любой тип в поле.
То есть проверка в рантайме появляется не от аннотаций, а от конкретной библиотеки, которая решила их читать.
Что выбрать под задачу
| Задача | Инструмент | Проверка в рантайме |
|---|---|---|
| Ловить ошибки типов до запуска | mypy / pyright | нет, только статически |
| Валидировать входящие данные (API, конфиги) | Pydantic v2 | да |
| Разбор и валидация HTTP-запросов | FastAPI | да |
| Компактно описать структуру данных без валидации | dataclasses | нет |
| Подсказки в редакторе | Pylance / PyCharm | нет |
Критерий короткий: если данные приходят извне и им нельзя доверять — нужна библиотека с рантайм-валидацией, аннотаций и mypy недостаточно. Если данные внутренние и вы контролируете их создание — хватает статической проверки.
Что делать дальше
Если проект без типов — не аннотируйте всё разом. Начните с публичных сигнатур модулей и границ, где приходят внешние данные. Подключите mypy в нестрогом режиме, добейтесь нуля ошибок на уже размеченном коде, потом включайте флаги строгости по одному. Настройки mypy живут в pyproject.toml или mypy.ini — актуальные имена опций смотрите в документации mypy, они менялись между версиями. В прод-коде, который читает данные из сети, ставьте Pydantic на границу и доверяйте только тому, что прошло валидацию.
Проектная работа: От проблемы до защиты: продукт, этапы, вопросы комиссии
Спросить за 15 ₽Источники: typing — Support for type hints (документация Python), mypy documentation
Частые вопросы
Замедляют ли аннотации выполнение программы?
Если у меня Python 3.8, синтаксис list[int] сработает?
Я расставил типы, а mypy ничего не находит — почему?
Аннотации гарантируют, что в поле не попадёт неправильный тип?
Стоит ли добавлять типы в старый проект без них?
Чем mypy отличается от pyright?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.