Справочник по Python · модуль 6 · функции и модули
Глава 6.11
Аннотации типов
Аннотация указывает, какой тип ожидается у параметра, возвращаемого значения или переменной. На выполнение программы она не влияет: это документация, которую читают человек и анализатор кода.
- Категория
- Функции и типы
- Уровень
- Средний
- Версия
- Python 3.10+ для записи
int | str - Зачем это знать
- Делать назначение функции понятным без чтения её тела
§1Зачем нужны аннотации
По заголовку функции без аннотаций не видно, с чем она работает. Строка def add(a, b) одинаково подходит и для чисел, и для строк, и для списков.
def add(a, b) что можно передать? неизвестно def add(a: int, b: int) ожидаются целые числа
Аннотация типа — это пометка о том, какой тип значения ожидается. Она решает три задачи: документирует код, позволяет редактору подсказывать методы и даёт статическим анализаторам вроде mypy находить несоответствия до запуска программы.
§2Синтаксис
Тип параметра пишут после имени через двоеточие, а тип результата — после стрелки -> перед двоеточием заголовка.
def add(a: int, b: int) -> int:
│ │ └ тип возвращаемого значения
└───────┴ типы параметров
def add(a: int, b: int) -> int: return a + b print(add(2, 3))
5
Использовать можно любые встроенные типы: int, float, str, bool, list, dict и другие. Имя типа пишется без кавычек и без скобок.
§3Аннотация не проверяет тип
Это главное, что нужно понять про аннотации: Python их не исполняет. Функция с аннотацией int спокойно примет строки.
def add(a: int, b: int) -> int: return a + b print(add("Py", "thon"))
Python
Редактор или mypy подчеркнут такой вызов, но программа от этого хуже работать не станет и не упадёт. Аннотация — обещание разработчика, а не проверка во время выполнения. Если проверка типа действительно нужна, её пишут явно: if not isinstance(a, int): ...
Обратная сторона тоже верна: несоответствие типов может привести к настоящей ошибке позже — там, где значение не поддержит нужную операцию. Аннотации помогают заметить это заранее.
§4Возвращаемое значение и None
Если функция ничего не возвращает, её результат — None, и это записывают явно.
def greet(name: str) -> None: print("Привет,", name) greet("Аня")
Привет, Аня
Запись -> None означает «возвращать нечего». Она отличает такую функцию от той, где программист просто забыл указать тип результата.
§5Коллекции в аннотациях
Для коллекции полезно указать не только сам тип, но и тип элементов. Это делают квадратными скобками.
def average(values: list[int]) -> float: return sum(values) / len(values) print(average([1, 2, 3]))
2.0
| Аннотация | Что означает |
|---|---|
list[int] | список целых чисел |
tuple[int, str] | кортеж из двух элементов: число и строка |
dict[str, int] | словарь со строковыми ключами и целыми значениями |
set[str] | множество строк |
Встроенные типы принимают квадратные скобки начиная с Python 3.9. До этого требовался модуль typing: from typing import List и затем List[int]. В коде для старых версий такая запись ещё встречается.
§6Несколько типов через |
Когда допустимо несколько типов, их перечисляют через вертикальную черту.
def process(value: int | str) -> None: if isinstance(value, int): print(value * 2) else: print(value.upper()) process(5) process("код")
10 КОД
Здесь аннотация и настоящая проверка работают вместе: int | str сообщает читателю о замысле, а isinstance() действительно разделяет случаи во время выполнения.
§7Значение может отсутствовать
Частный, но очень частый случай — функция возвращает результат либо None, если ничего не нашла.
def find_city(name: str) -> str | None: cities = {"Аня": "Москва"} return cities.get(name) print(find_city("Аня")) print(find_city("Борис"))
Москва None
Такая аннотация — предупреждение вызывающему коду: результат нужно проверить перед использованием. В старых версиях то же самое записывали как Optional[str] из модуля typing.
§8Аннотации переменных
Аннотировать можно не только функции. Переменной тип указывают так же — через двоеточие после имени.
x: int = 10 y: float = 5.5 z: str = "Hello" print(x, y, z)
10 5.5 Hello
То же работает внутри функции для локальных переменных: count: int = 0. Значение можно и не присваивать — запись total: int объявляет только тип, но саму переменную не создаёт, и обращение к ней вызовет NameError.
§9Атрибут __annotations__
Аннотации не исчезают при запуске: Python складывает их в словарь __annotations__, где ключи — имена, а значения — типы.
def add(a: int, b: int) -> int: return a + b print(add.__annotations__)
{'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}Тип возвращаемого значения лежит под специальным ключом 'return'. У классов есть свой такой словарь — по аннотациям атрибутов.
class Point: x: int y: int print(Point.__annotations__)
{'x': <class 'int'>, 'y': <class 'int'>}| Где объявлены | Как получить |
|---|---|
| параметры и результат функции | function.__annotations__ |
| атрибуты класса | ClassName.__annotations__ |
| переменные модуля | __annotations__ на уровне модуля |
В повседневном коде этот словарь почти не нужен. Он важен библиотекам, которые строят поведение по аннотациям, — так работают, например, валидация данных в Pydantic и внедрение зависимостей в FastAPI.
§10Где аннотации не нужны
Аннотации — инструмент, а не обязанность. Они окупаются там, где код читают другие или где типы неочевидны.
| Ситуация | Стоит ли аннотировать |
|---|---|
| функция в общем модуле проекта | да, это её документация |
| функция возвращает разные типы | да, обязательно — иначе неясно, что ожидать |
| короткий учебный скрипт | не обязательно |
x: int = 10 для очевидного литерала | лишний шум, тип и так виден |
Аннотация, которая не соответствует действительности, хуже её отсутствия: она вводит в заблуждение. Если тип изменился — исправьте аннотацию.
§11Частые ошибки
| Что написано | Что произойдёт | Как правильно |
|---|---|---|
ожидать, что a: int запретит строку | значение другого типа пройдёт без ошибки | проверять через isinstance(), если нужна гарантия |
def add(a: int, b: int) = int: | SyntaxError | тип результата пишут после -> |
a: "int" как обычная аннотация | в __annotations__ попадёт строка, а не тип | a: int без кавычек |
list[int] в Python 3.8 | TypeError: старые версии так не умеют | List[int] из typing или обновить Python |
int | None в Python 3.9 | TypeError | Optional[int] из typing или Python 3.10+ |
total: int и сразу print(total) | NameError: переменная не создана | присвоить значение: total: int = 0 |
| не обновить аннотацию после правки функции | документация начинает врать | менять аннотацию вместе с кодом |
§12Проверьте себя
Остановит ли аннотация a: int передачу строки?
Нет. Аннотации не проверяются во время выполнения — программа отработает как обычно, а несоответствие подчеркнёт только анализатор кода.
Что означает -> None?
Функция ничего не возвращает: её результат — None.
Как записать «строка или ничего»?
str | None начиная с Python 3.10; раньше — Optional[str] из модуля typing.
Под каким ключом лежит тип результата в __annotations__?
Под ключом 'return'.
Нужен ли typing для list[int]?
Начиная с Python 3.9 — нет, встроенные типы принимают квадратные скобки сами.
§13Шпаргалка
def f(a: int):def f() -> int:-> Nonelist[int]dict[str, int]int | strstr | Nonex: int = 10f.__annotations__Итог главы
Аннотации описывают ожидаемые типы параметров, результата и переменных. Они не влияют на выполнение: программа с «неправильным» типом отработает как обычно. Ценность аннотаций в другом — они документируют замысел, помогают редактору и позволяют статическому анализатору находить несоответствия до запуска.