Типизация в Python: аннотации типов typing — понятное руководство с примерами
Типизация в Python — это способ описать ожидаемые типы аргументов, возвратов функций и переменных с помощью аннотаций. Они не меняют поведение программы на рантайме (по умолчанию), но позволяют IDE и статическим анализаторам (mypy, pyright) предупреждать о потенциальных ошибках ещё до запуска кода.
Быстрый старт: простые аннотации
Аннотации — это подсказки. Они не обязательны, но крайне полезны для читабельности и проверки типов.
def greet(name: str) -> str:
return f"Привет, {name}!"
age: int = 30
prices: list[float] = [9.99, 12.5, 3.0] # Python 3.9+: встроенные коллекции как обобщённые типы
profile: dict[str, int] = {"posts": 10, "likes": 120}
С Python 3.10 можно использовать оператор | вместо Union, а с 3.9 — нативные обобщения коллекций (list[str] вместо typing.List[str]).
Union, Optional и None
Частая потребность — указать, что значение может быть одного из нескольких типов, или быть None.
# Эквивалентно Union[int, str]
from typing import Optional
def parse_id(raw: int | str) -> int:
return int(raw)
def find_price(code: str | None) -> Optional[float]:
if code is None:
return None
return 9.99
Запомнить просто: Optional[T] — это T | None. Если аргумент по умолчанию None, аннотируйте это явно.
Аннотации переменных и литералы типов
from typing import Final, Literal
PI: Final = 3.14159 # Нельзя переназначать (для анализаторов)
status: Literal["ok", "err"] = "ok" # Разрешены только перечисленные строковые литералы
Literal сужает набор допустимых значений и помогает анализаторам ловить опечатки и неверные ветки условий.
Структуры данных: TypedDict и dataclass
Если вы храните структурированные данные в словаре, используйте TypedDict для явного описания ключей и типов значений.
from typing import TypedDict
class UserTD(TypedDict):
id: int
name: str
is_active: bool
user: UserTD = {"id": 1, "name": "Alex", "is_active": True}
Для объектов — dataclass: компактно и читаемо.
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
is_active: bool = True
def activate(u: User) -> None:
u.is_active = True
Протоколы и утиная типизация: Protocol
Protocol описывает набор методов/атрибутов, не требуя общего базового класса. Это «утиная типизация» с подсказками.
from typing import Protocol
class SupportsClose(Protocol):
def close(self) -> None: ...
def safe_close(obj: SupportsClose) -> None:
obj.close()
class FileLike:
def close(self) -> None:
print("closed")
safe_close(FileLike()) # Ок: у класса есть метод close
Обобщения (Generic), TypeVar и NewType
Generic позволяет писать обобщённый код, сохраняющий информацию о типах.
from typing import TypeVar, Generic, NewType
T = TypeVar("T")
class Box(Generic[T]):
def __init__(self, value: T):
self.value = value
def get(self) -> T:
return self.value
int_box = Box[int](10)
str_box = Box[str]("hi")
UserId = NewType("UserId", int)
def load_user(user_id: UserId) -> dict:
return {"id": int(user_id), "name": "Alex"}
load_user(UserId(5)) # Ок
# load_user(5) # Анализатор отметит как ошибку типов
Сужение типа (type narrowing) и cast
Анализаторы умеют сужать тип после проверок isinstance/if is not None. Если нужно подсказать анализатору руками — используйте cast осторожно.
from typing import cast
def total(x: int | None) -> int:
if x is None:
return 0
return x * 2 # здесь анализатор уже видит x как int
def get_cfg(raw: object) -> dict[str, str]:
# Знаем из контекста, что это словарь строк
return cast(dict[str, str], raw)
Тип Self и метод-цепочки
В Python 3.11 добавили typing.Self для методов, возвращающих self. На более старых версиях используйте TypeVar с bound.
# В Python 3.11+
from typing import Self
class Builder:
def set_x(self, x: int) -> Self:
self.x = x
return self
# Альтернатива для более ранних версий:
# from typing import TypeVar
# TSelf = TypeVar("TSelf", bound="Builder")
# class Builder:
# def set_x(self, x: int) -> TSelf: ...
Инструменты проверки: mypy и pyright
Статические анализаторы читают аннотации и предупреждают о несоответствиях.
# Установка
pip install mypy
# или
pip install pyright # через pipx: pipx install pyright
# Запуск
mypy src/ # проверка кода в папке src
pyright # pyright ищет конфиг pyproject.toml/pyrightconfig.json
Пример настроек для mypy в pyproject.toml:
[tool.mypy]
python_version = "3.11"
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_any_generics = true
no_implicit_optional = true
strict_optional = true
ignore_missing_imports = true
Советы и лучшие практики
- Аннотируйте публичные функции и методы, а также модули утилит. Для внутреннего кода — постепенно.
- Избегайте Any — он «ломает» проверку типов. Если вынуждены — локализуйте его и добавляйте комментарии почему.
- Используйте Optional[T] или T | None явно, не прячьте None в сигнатурах.
- Предпочитайте Protocol вместо общих базовых классов, когда важны поведение/интерфейс, а не наследование.
- Выбирайте нативные аннотации: list[str], dict[str, int] (Python 3.9+) и оператор | (3.10+).
- Для форвард-ссылок (класс ещё не объявлен) используйте строки: "User" или включайте будущие аннотации, если ваша версия это поддерживает.
- Поднимайте строгость в mypy/pyright итеративно: добавляйте правила и исправляйте предупреждения по мере готовности команды.
Частые ошибки
- Путаница Optional[T] и Union[T]. Optional[T] всегда означает T | None.
- Использование or вместо | для объединений: нужно писать int | str, а не int or str.
- Отсутствие аннотаций у публичных API — теряется польза типов в интеграциях.
- Скрытые Any из сторонних библиотек — добавляйте типы-обертки или игнорируйте импорт точечно, а не глобально.
Итог
Аннотации и модуль typing — мощный инструмент, который повышает качество кода без накладных расходов на рантайме. Начните с простого: аннотируйте функции и структуры данных, подключите mypy или pyright, постепенно включайте строгие правила. Уже через пару спринтов вы заметите меньше регрессий и больше уверенности при рефакторинге.
Хотите системно прокачать навыки и быстро перейти от основ к уверенной разработке? Загляните в практический курс «Python с Нуля до Гуру» — программа, примеры и первые шаги здесь.
-
Создано 13.02.2026 17:03:30
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.