Модуль 6 · Функции и модули

Справочник по Python · модуль 6 · функции и модули

Глава 6.11

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:
           │       │       └ тип возвращаемого значения
           └───────┴ типы параметров
main.pyсложение двух целых
def add(a: int, b: int) -> int:
    return a + b

print(add(2, 3))
Выводобычный вызов
5

Использовать можно любые встроенные типы: int, float, str, bool, list, dict и другие. Имя типа пишется без кавычек и без скобок.

§3Аннотация не проверяет тип

Это главное, что нужно понять про аннотации: Python их не исполняет. Функция с аннотацией int спокойно примет строки.

main.pyпереданы строки вместо чисел
def add(a: int, b: int) -> int:
    return a + b

print(add("Py", "thon"))
Выводошибки нет, сработала конкатенация
Python
«Ошибка» здесь — это предупреждение

Редактор или mypy подчеркнут такой вызов, но программа от этого хуже работать не станет и не упадёт. Аннотация — обещание разработчика, а не проверка во время выполнения. Если проверка типа действительно нужна, её пишут явно: if not isinstance(a, int): ...

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

§4Возвращаемое значение и None

Если функция ничего не возвращает, её результат — None, и это записывают явно.

main.pyфункция только печатает
def greet(name: str) -> None:
    print("Привет,", name)

greet("Аня")
Выводрезультата нет, есть побочный эффект
Привет, Аня

Запись -> None означает «возвращать нечего». Она отличает такую функцию от той, где программист просто забыл указать тип результата.

§5Коллекции в аннотациях

Для коллекции полезно указать не только сам тип, но и тип элементов. Это делают квадратными скобками.

main.pyсписок целых на входе, дробное на выходе
def average(values: list[int]) -> float:
    return sum(values) / len(values)

print(average([1, 2, 3]))
Выводделение всегда даёт float
2.0
АннотацияЧто означает
list[int]список целых чисел
tuple[int, str]кортеж из двух элементов: число и строка
dict[str, int]словарь со строковыми ключами и целыми значениями
set[str]множество строк
Так было не всегда

Встроенные типы принимают квадратные скобки начиная с Python 3.9. До этого требовался модуль typing: from typing import List и затем List[int]. В коде для старых версий такая запись ещё встречается.

§6Несколько типов через |

Когда допустимо несколько типов, их перечисляют через вертикальную черту.

main.pyчисло или строка
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, если ничего не нашла.

main.pyпоиск может не дать результата
def find_city(name: str) -> str | None:
    cities = {"Аня": "Москва"}
    return cities.get(name)

print(find_city("Аня"))
print(find_city("Борис"))
Выводво втором случае значения нет
Москва
None

Такая аннотация — предупреждение вызывающему коду: результат нужно проверить перед использованием. В старых версиях то же самое записывали как Optional[str] из модуля typing.

§8Аннотации переменных

Аннотировать можно не только функции. Переменной тип указывают так же — через двоеточие после имени.

main.pyаннотации на уровне модуля
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__, где ключи — имена, а значения — типы.

main.pyаннотации функции
def add(a: int, b: int) -> int:
    return a + b

print(add.__annotations__)
Выводпараметры и результат
{'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}

Тип возвращаемого значения лежит под специальным ключом 'return'. У классов есть свой такой словарь — по аннотациям атрибутов.

main.pyаннотации атрибутов класса
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.8TypeError: старые версии так не умеютList[int] из typing или обновить Python
int | None в Python 3.9TypeErrorOptional[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:
Ничего не возвращает-> None
Коллекцияlist[int]dict[str, int]
Несколько типовint | str
Может отсутствоватьstr | None
Переменнаяx: int = 10
Словарь аннотацийf.__annotations__

Итог главы

Аннотации описывают ожидаемые типы параметров, результата и переменных. Они не влияют на выполнение: программа с «неправильным» типом отработает как обычно. Ценность аннотаций в другом — они документируют замысел, помогают редактору и позволяют статическому анализатору находить несоответствия до запуска.

Глава 6.11 · Аннотации типов · Справочник по Python Автор: Макаров Максим Николаевич