Аннотации типов (Type Hints) в Python

17 июля 2026
Автор

Антон Ларичев

Что такое аннотации типов

Python — язык с динамической типизацией: переменные не требуют объявления типа, а их тип определяется во время выполнения. Аннотации типов (Type Hints) — это необязательный механизм, добавленный в Python 3.5 (PEP 484), который позволяет явно указывать типы переменных, параметров функций и возвращаемых значений.

Аннотации не влияют на выполнение программы — интерпретатор Python их игнорирует. Их цель — улучшить читаемость кода, помочь IDE при автодополнении и дать статическим анализаторам (mypy, pyright, pylance) возможность находить ошибки ещё до запуска.

# Без аннотаций
def add(a, b):
    return a + b

# С аннотациями
def add(a: int, b: int) -> int:
    return a + b

Второй вариант сразу сообщает читателю: функция принимает два целых числа и возвращает целое число.

Базовый синтаксис

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

name: str = "Alice"
age: int = 30
price: float = 9.99
is_active: bool = True

Аннотацию можно поставить даже без присвоения значения — это используется в классах:

class User:
    id: int
    name: str
    email: str

Аннотации параметров функций

Тип параметра указывается через двоеточие после имени:

def greet(name: str, age: int) -> str:
    return f"Hello, {name}! You are {age} years old."

Возвращаемый тип

Тип возвращаемого значения указывается через -> перед двоеточием тела функции:

def get_square(n: int) -> int:
    return n * n

def say_hello() -> None:
    print("Hello!")

None используется, когда функция ничего не возвращает (аналог void в других языках).

Модуль typing

Для сложных типов используется стандартный модуль typing. Начиная с Python 3.9 и 3.10 многие конструкции доступны напрямую без импорта из typing, но для совместимости с Python 3.8 импорт всё ещё актуален.

List, Dict, Tuple, Set

from typing import List, Dict, Tuple, Set

def process_names(names: List[str]) -> None:
    for name in names:
        print(name)

def get_user_map() -> Dict[str, int]:
    return {"alice": 1, "bob": 2}

def get_coords() -> Tuple[float, float]:
    return (55.75, 37.61)

def unique_tags(tags: Set[str]) -> Set[str]:
    return tags

Начиная с Python 3.9 можно использовать встроенные типы напрямую:

# Python 3.9+
def process_names(names: list[str]) -> None:
    for name in names:
        print(name)

def get_user_map() -> dict[str, int]:
    return {"alice": 1, "bob": 2}

Optional

Optional[X] означает, что значение может быть либо X, либо None:

from typing import Optional

def find_user(user_id: int) -> Optional[str]:
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

С Python 3.10 можно использовать оператор |:

# Python 3.10+
def find_user(user_id: int) -> str | None:
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

Union

Union[X, Y] означает, что значение может быть одного из нескольких типов:

from typing import Union

def process_input(value: Union[int, str]) -> str:
    return str(value)

# Python 3.10+
def process_input(value: int | str) -> str:
    return str(value)

Any

Any отключает проверку типов для конкретного значения — полезно при интеграции с нетипизированным кодом:

from typing import Any

def log(data: Any) -> None:
    print(data)

Используйте Any осторожно: злоупотребление им нивелирует пользу от аннотаций.

Callable

Для аннотирования функций как аргументов используется Callable:

from typing import Callable

def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
    return func(a, b)

def multiply(x: int, y: int) -> int:
    return x * y

result = apply(multiply, 3, 4)  # 12

Callable[[int, int], int] означает: функция принимает два int и возвращает int.

TypeVar и обобщённые функции

TypeVar позволяет писать обобщённые (generic) функции, которые работают с любым типом, сохраняя при этом связь между входным и выходным типами:

from typing import TypeVar, List

T = TypeVar("T")

def first(items: List[T]) -> T:
    return items[0]

name = first(["Alice", "Bob"])   # str
number = first([1, 2, 3])        # int

Статический анализатор понимает, что если передать List[str], то вернётся str, а не Any.

Аннотации в классах

Обычные атрибуты

class Product:
    name: str
    price: float
    in_stock: bool

    def __init__(self, name: str, price: float, in_stock: bool = True) -> None:
        self.name = name
        self.price = price
        self.in_stock = in_stock

    def apply_discount(self, percent: float) -> float:
        return self.price * (1 - percent / 100)

ClassVar

ClassVar обозначает атрибут класса, а не экземпляра:

from typing import ClassVar

class Config:
    MAX_RETRIES: ClassVar[int] = 3
    timeout: int

    def __init__(self, timeout: int) -> None:
        self.timeout = timeout

dataclasses

Аннотации типов особенно удобны с dataclasses — они заменяют ручное написание __init__:

from dataclasses import dataclass
from typing import Optional

@dataclass
class User:
    id: int
    name: str
    email: str
    age: Optional[int] = None

user = User(id=1, name="Alice", email="alice@example.com")

TypedDict

TypedDict позволяет описать структуру словаря с конкретными ключами и типами значений:

from typing import TypedDict

class Movie(TypedDict):
    title: str
    year: int
    rating: float

def print_movie(movie: Movie) -> None:
    print(f"{movie['title']} ({movie['year']}) — {movie['rating']}")

film: Movie = {"title": "Inception", "year": 2010, "rating": 8.8}
print_movie(film)

Это удобнее, чем Dict[str, Any], потому что анализатор проверяет конкретные ключи и их типы.

Protocol — структурная типизация

Protocol позволяет описывать интерфейсы без наследования (утиная типизация с проверкой типов):

from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None:
        ...

class Circle:
    def draw(self) -> None:
        print("Drawing circle")

class Square:
    def draw(self) -> None:
        print("Drawing square")

def render(shape: Drawable) -> None:
    shape.draw()

render(Circle())  # OK
render(Square())  # OK

Circle и Square не наследуют Drawable, но удовлетворяют протоколу, потому что реализуют метод draw. Это ключевое отличие от абстрактных базовых классов.

Literal и Final

Literal

Literal ограничивает значения конкретным набором:

from typing import Literal

def set_direction(direction: Literal["left", "right", "up", "down"]) -> None:
    print(f"Moving {direction}")

set_direction("left")      # OK
set_direction("diagonal")  # Ошибка при статическом анализе

Final

Final указывает, что значение не должно переопределяться:

from typing import Final

MAX_SIZE: Final = 100
API_VERSION: Final[str] = "v2"

Проверка типов с mypy

Аннотации сами по себе не защищают от ошибок — для этого нужен статический анализатор. Самый популярный — mypy.

Установка:

pip install mypy

Проверка файла:

mypy main.py

Пример обнаружения ошибки:

# main.py
def greet(name: str) -> str:
    return "Hello, " + name

greet(123)  # mypy сообщит об ошибке
main.py:4: error: Argument 1 to "greet" has incompatible type "int"; expected "str"

Настройка через mypy.ini:

[mypy]
python_version = 3.11
strict = True
ignore_missing_imports = True

Флаг strict включает наиболее строгие проверки, в том числе запрет на неявный Any и требование аннотаций для всех публичных функций.

Отложенные аннотации

Иногда тип ссылается на класс, который ещё не объявлен. Это решается строковой аннотацией или директивой from __future__ import annotations:

from __future__ import annotations

class Node:
    def __init__(self, value: int, next: Node | None = None) -> None:
        self.value = value
        self.next = next

Без этого импорта Python выдал бы NameError, потому что Node ещё не определён в момент разбора аннотации. Директива переводит все аннотации в строковый режим и откладывает их вычисление.

Практический пример: сервис обработки заказов

from __future__ import annotations
from dataclasses import dataclass, field
from typing import Optional, Callable, TypedDict

class OrderItem(TypedDict):
    product_id: int
    quantity: int
    unit_price: float

@dataclass
class Order:
    order_id: int
    customer_name: str
    items: list[OrderItem] = field(default_factory=list)
    discount: float = 0.0

    def total(self) -> float:
        subtotal = sum(item["quantity"] * item["unit_price"] for item in self.items)
        return subtotal * (1 - self.discount)

    def add_item(self, item: OrderItem) -> None:
        self.items.append(item)

ApplyDiscount = Callable[[Order, float], None]

def apply_promo(order: Order, percent: float) -> None:
    order.discount = percent / 100

def process_orders(
    orders: list[Order],
    discount_fn: Optional[ApplyDiscount] = None,
) -> dict[int, float]:
    result: dict[int, float] = {}
    for order in orders:
        if discount_fn:
            discount_fn(order, 10.0)
        result[order.order_id] = order.total()
    return result

Здесь аннотации документируют контракт каждого компонента: что принимает process_orders, что возвращает total, каков формат OrderItem. Разработчик, открывший этот файл впервые, мгновенно понимает структуру данных без чтения реализации.

Итого

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

  • Базовые типы (int, str, float, bool, None) — для простых случаев
  • list, dict, tuple, set — для коллекций (Python 3.9+)
  • Optional / X | None — для значений, которые могут отсутствовать
  • Union / X | Y — для значений нескольких типов
  • TypeVar — для обобщённых функций
  • Protocol — для структурной типизации без наследования
  • TypedDict — для типизированных словарей
  • Literal и Final — для ограничения значений и констант
  • mypy — для статической проверки

Начните с аннотирования публичных функций и методов — это даст максимальную пользу при минимальных усилиях. Постепенно добавляйте strict = True в конфигурацию mypy и следите за отчётами анализатора.

Подробнее про Python и написание production-кода на нём — на курсе Python на PurpleSchool.

Стрелочка влевоОператор моржа := в PythonPython Enum — перечисления в PythonСтрелочка вправо

Постройте личный план изучения Python до уровня Middle — бесплатно!

Python — часть карты развития Backend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Все гайды по Python

Как отправлять запросы с помощью requests в PythonПочему Python выводит значение без команды printКак работает команда print в PythonВозможности Python для автоматизации задачРабота с JSON в Python на примерахPython get — методы получения данныхКак находить и исправлять ошибки в PythonРабота с данными через API и внешние сервисыСтруктура и оформление кода PythonОсновы Django с PythonПолезные приёмы в Python для повседневной работыИспользование locals в Python для отладкиИнтеграция PHP и PythonКак выполнять HTTPS-запросы в PythonFastAPI Python — быстрый старт: создание REST API с нуляКак работать с API в Python
Ввод целого числа в PythonВедение логов в PythonУдаление данных в Python с помощью removeОбработка исключений с помощью try/except в PythonФункция super() в Python — как вызвать метод родителяСоздание собственных контекстных менеджеров в PythonРабота с символами программирования PythonРабота с переменной X в PythonРабота с классами в PythonКак скачать Python на компьютерПростая программа на Python для начинающихОсновы Python для тех, кто начинаетЧто нового в Python 3Поддерживается ли Python 2 и стоит ли его использоватьPython 1 — с чего начиналась история языкаКоманда python print - полное руководство по выводу данныхПравила именования переменных в PythonПользовательские исключения в PythonОсновы Python coreОписание объектов PythonНаследование классов в Python — основы и примерыМножественное наследование в Python — примеры и MROКонтекстный менеджер with в Python — как работает и зачем нуженКомментарии в Python — однострочные, многострочные и docstringКакой Python выбрать для установкиКак вывести целое число с помощью print в PythonКак установить Python на Windows macOS и LinuxКак пользоваться консолью PythonКак получить последний элемент в PythonКак найти значение в PythonКак настроить PythonКак использовать print для строк в PythonКак работает интерпретатор PythonИнструкция по работе с PythonБлок finally в обработке исключений PythonЦелые числа в PythonАбстрактные классы в Python — ABC и abstractmethod
Pydantic: валидация данных в Python
Загрузка данных PythonУправление проектами на GitHub с PythonСоздание веб-приложений на Flask PythonСоздание бота на PythonСоздание интерфейсов Python QTСоздание игр с PygameСоздание GUI в PythonКак работать со словарями в PythonРабота с библиотеками через Python PackagingРабота со временем в Python при помощи модуля timePython name — особенности переменнойМатематические операции в Python с модулем mathPython listing — что это и как использоватьPytest — тестирование на Python: полное руководствоОбработка изображений с OpenCV PythonNumPy в Python — основы и применение в задачахМашинное обучение с PythonИспользование Anaconda с PythonМодуль contextlib в Python — утилиты для контекстных менеджеровБиблиотеки Python и их применение в проектах
Возврат значений из функции в PythonВложенные функции в PythonСоздание собственных декораторов в PythonРабота с функцией map в PythonЦикл while в Python и примеры использованияОбработка чисел, введённых через input в PythonОсновные операторы в Python с примерамиУсловные выражения if else в Python для начинающихКак выполняется вызов функций call в PythonПродвинутые генераторы в Python — send, throw, close и корутиныПозиционные и именованные аргументы в PythonОбъявление переменных и управление областью видимости в PythonПередача аргументов по ссылке и по значению в PythonПередача аргументов через args и kwargs в PythonОсновные методы Python и примеры их использованияОператор match/case в Python 3.10+ — основы структурного сопоставленияПаттерны match/case в Python — деструктуризация, guard и вложенные шаблоныПрактические примеры match/case в Python — реальные сценарии примененияЛокальные и глобальные переменные в PythonЧасто используемые команды PythonКлючевые слова global и nonlocal в PythonКак создавать функции в PythonКак работает сборщик мусора в PythonКак работает область видимости переменных в PythonКак работает функция callable в PythonКак работает функция any и all в PythonКак проверить тип переменной в PythonКак передать функцию как аргумент в PythonКак использовать функцию isinstance в PythonКак использовать функцию filter в PythonКак использовать функцию filter в PythonКак использовать функцию eval безопасно в PythonКак использовать декораторы в PythonИзменяемые и неизменяемые типы данных в PythonГенераторы и yield в Python — как создавать и использоватьГенераторные выражения в Python — синтаксис и примерыФункции в Python и способы их вызоваФункции как объекты в PythonЧто такое замыкания в PythonЧто делает функция reduce в PythonЧто делает функция id в PythonАргументы по умолчанию в PythonАнонимные функции и lambda в PythonАлгоритмы на Python — примеры и объяснение
Запись данных в PythonУстановка pip в PythonУправление зависимостями requirement в PythonУправление библиотеками с помощью Python PackagingУдаление пробелов с помощью strip в PythonСтруктурирование кода в PythonСоздание исполняемого файла Python в exeРазбор traceback в модуле PythonРазбор site-packages в PythonРазбор Program Files в PythonРабота с Unicode кодировками в PythonРабота с системными функциями Python sysРабота с папкой AppData в PythonРабота с модулем logging в PythonРабота с каталогами в PythonРабота с CSV в PythonВиртуальная среда venv в Python — создание и настройкаКак создать простое приложение на PythonИспользование pip в Python для установки пакетовМодули в Python и организация кода в проектеИмпорт модулей в Python и правила подключенияРабота с файлами в Python пошаговоЧто делает компилятор Python и как он работаетПолучение строки из модуля PythonПодключение файлов в Python с includeПеременные среды в PythonСборка проекта с помощью packaging в PythonНастройка Python сервераИспользование Python на UbuntuИспользование консоли PythonИспользование кодировок в PythonИнициализация пакетов PythonИмпорт модулей PythonИмпорт имен в PythonСреда IDLE Python и базовые возможностиЧтение и запись TXT в PythonЧтение файлов в Python с помощью open file
Удаление элементов из списка PythonТипы данных в Python — обзор и рекомендацииОсновные операции со строками в PythonМетоды str в Python и обработка текстаСписки в Python и их ключевые методыСоздание списков данных в PythonРабота со строками и символами в PythonРабота со столбцами в PythonРабота со списком значений в PythonРабота с таблицами в Python с помощью DataFrameРабота с RFR в PythonРабота с пробелами в PythonРабота с массивами в PythonРабота с кортежами tuple PythonРабота с координатами X и Y в PythonРабота с ключами в PythonРабота с элементами данных PythonРабота с двоичными числами PythonРабота с данными в PythonРабота с данными NumPy PythonРабота с большими числами в PythonРабота с битами в PythonРабота с байтами в PythonЧто такое значение в Python и как его определитьМножества в Python и операции с нимиИспользование range в Python для цикловПроверка на четность в PythonПроверка числа в PythonПреобразование типов в PythonПреобразование списка в строку PythonПреобразование числа в строку в PythonПостроение графиков в PythonОпределение индекса элемента в PythonОкругление чисел в PythonОбъединение списков в Python с помощью zipМножества в PythonМассивы в Python и отличие от списковМассив чисел в PythonКортежи данных в PythonКак вычислить сумму чисел в PythonКак получить остаток от деления в PythonКак найти следующее число в PythonИспользование Unicode в PythonТип int в Python и его особенностиИндекс списка в PythonФункции для работы со строками в PythonЭлементы Python и способы доступа к нимДоступ к элементам массива в PythonДеление чисел в PythonРабота с данными в Python на практикеКак работать с числами в Python
Открыть базу знаний

Лучшие курсы по теме

Иконка молнииНовый
изображение курса

Основы Python

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий