Основы
- Что это? Подсказки (hints) для статических анализаторов и IDE, указывающие ожидаемые типы данных. Не влияют на выполнение кода по умолчанию.
- Зачем? Улучшение читаемости, раннее обнаружение ошибок, помощь IDE (автодополнение, рефакторинг), самодокументирование.
- Синтаксис:
* Переменные:имя: тип = значение
* Функции:def func(аргумент: тип_аргумента) -> тип_возврата:
* Если функция ничего не возвращает:-> None
Базовые и Встроенные Типы
- Примитивы:
int
,float
,str
,bool
,bytes
. None
: Тип для значенияNone
.- Встроенные Generic-типы (Python 3.9+):
list[T]
: Список элементов типаT
. Пример:numbers: list[int] = [1, 2]
dict[K, V]
: Словарь с ключами типаK
и значениями типаV
. Пример:ages: dict[str, int] = {"Alice": 30}
tuple[T1, T2, ...]
: Кортеж с фиксированным набором типов. Пример:point: tuple[int, float] = (10, 0.5)
tuple[T, ...]
: Кортеж с переменным числом элементов одного типа. Пример:coords: tuple[int, ...] = (1, 2, 3)
set[T]
: Множество элементов типаT
. Пример:unique_names: set[str] = {"Bob", "Eve"}
type[C]
: Тип для самого классаC
. Пример:my_class: type[MyClass] = MyClass
Почему именно такие буквы?
T(type) для универсального типа
U(next letter after T) для второго универсального типа
K(key) для ключа
V(value) для значения (в паре с K)
E(element) для элемента коллекции
N(number) для числа
C(class) для класса
Оператор Объединения (Union Pipe) (Python 3.10+)
T1 | T2
: Значение может быть типаT1
илиT2
.- Пример:
identifier: int | str = "id123"
- Пример:
Optional[T]
эквивалентенT | None
.- Пример:
user_id: int | None = None
(предпочтительнееOptional[int]
)
- Пример:
Специальные Типы из Модуля typing
-
Any
: Любой тип. Фактически отключает проверку типов для данной аннотации. Использовать с осторожностью.from typing import Any data: Any = get_untyped_data()
-
Callable[[Arg1Type, Arg2Type, ...], ReturnType]
: Тип для функций и других вызываемых объектов.Callable[[], str]
: Функция без аргументов, возвращающая строку.Callable[[int, str], bool]
: Функция, принимающаяint
иstr
, возвращающаяbool
.Callable[..., ReturnType]
: Функция с любыми аргументами, возвращающаяReturnType
.
from typing import Callable def process(text: str, num: int) -> bool: return len(text) > num my_func: Callable[[str, int], bool] = process
-
TypeVar
: Переменная типа, используется для создания Generic-функций и классов.from typing import TypeVar T = TypeVar('T') # Может быть любым типом K = TypeVar('K', bound=str) # Должен быть str или его подтипом V = TypeVar('V', str, bytes) # Должен быть str или bytes (ковариантный)
-
Generic
: Базовый класс для создания Generic-классов.from typing import TypeVar, Generic T = TypeVar('T') class Container[T]: # Python 3.12+ синтаксис def __init__(self, item: T): self.item = item def get_item(self) -> T: return self.item # Для Python < 3.12: # class Container(Generic[T]): # ... c_int: Container[int] = Container(10) c_str: Container[str] = Container("hello")
Generic-функции:
from typing import TypeVar T = TypeVar('T') def first_element[T](items: list[T]) -> T: # Python 3.12+ синтаксис return items[0] # Для Python < 3.12: # def first_element(items: list[T]) -> T: # return items[0] result_int: int = first_element([1, 2, 3]) result_str: str = first_element(["a", "b"])
-
Protocol
: Для определения протоколов (структурная типизация / “утиная типизация” для статического анализатора). Класс не обязан наследоваться от протокола, достаточно реализовать его методы/атрибуты.from typing import Protocol class SupportsClose(Protocol): def close(self) -> None: ... # '...' означает, что реализация не важна class ResourceManager: def close(self) -> None: print("Resource closed") def cleanup(obj: SupportsClose) -> None: obj.close() cleanup(ResourceManager()) # Работает, т.к. ResourceManager "структурно" соответствует SupportsClose
Можно добавлять
@runtime_checkable
дляisinstance
проверок (с осторожностью). -
Literal[*values]
: Указывает, что переменная может принимать только одно из перечисленных литеральных значений.from typing import Literal Mode = Literal["r", "w", "a", "r+", "w+", "a+"] def open_file(file: str, mode: Mode) -> None: ... open_file("log.txt", "r") # open_file("log.txt", "read") # Ошибка типа
-
Final
: Указывает, что переменная не может быть переназначена, а метод не может быть переопределен в подклассе.from typing import final, Final API_KEY: Final[str] = "supersecret" # API_KEY = "new_key" # Ошибка типа class Base: @final def cannot_override(self) -> None: ...
-
ClassVar
: Указывает, что атрибут является переменной класса, а не экземпляра.from typing import ClassVar class MyObject: instance_count: ClassVar[int] = 0 def __init__(self): MyObject.instance_count += 1
-
TypedDict
: Для аннотирования словарей с известным набором строковых ключей и типами значений.from typing import TypedDict, NotRequired # NotRequired с Python 3.11+ # Для Python < 3.11 можно использовать total=False # class Movie(TypedDict, total=False): # title: str # year: NotRequired[int] class Movie(TypedDict): title: str year: int director: NotRequired[str] # Python 3.11+ для NotRequired movie: Movie = {"title": "Inception", "year": 2010} # movie_invalid: Movie = {"name": "Inception"} # Ошибка типа: ключ 'name', а не 'title'
total=False
(вclass Movie(TypedDict, total=False):
) делает все поля необязательными.NotRequired[T]
(Python 3.11+) делает конкретное поле необязательным. -
NewType
: Создает “различимый” подтип существующего типа. Полезно для семантического разделения типов, которые структурно одинаковы.from typing import NewType UserId = NewType('UserId', int) ProductId = NewType('ProductId', int) def get_user(user_id: UserId) -> None: ... def get_product(prod_id: ProductId) -> None: ... uid = UserId(123) pid = ProductId(123) get_user(uid) # get_user(pid) # Ошибка типа, хотя оба int # get_user(123) # Ошибка типа, нужен UserId
-
Self
(Python 3.11+): Аннотирует методы, возвращающие экземпляр своего класса (или его подкласса). Полезно для chainable методов и фабричных методов в классах.from typing import Self # Python 3.11+ class Person: def __init__(self, name: str): self.name = name def set_age(self, age: int) -> Self: self.age = age return self # Возвращает экземпляр Person или его подкласса
-
TypeAlias
(Python 3.10+): Явный способ объявления псевдонимов типов. Улучшает читаемость для сложных аннотаций.from typing import TypeAlias # Python 3.10+ Vector: TypeAlias = list[float] ConnectionOptions: TypeAlias = dict[str, str] Point: TypeAlias = tuple[int, int] def process_vector(v: Vector) -> None: ...
До Python 3.10 псевдонимы создавались простым присваиванием:
Vector = list[float]
.TypeAlias
делает намерение более явным для статических анализаторов.
Расширенные Generic-типы (Python 3.12+)
- Более компактный синтаксис для объявления Generic-классов, функций и псевдонимов типов:
# Generic Класс (Python 3.12+) class MyList[T]: def __init__(self, initial: list[T]): self._items = initial def get_first(self) -> T: return self._items[0] # Generic Функция (Python 3.12+) def make_pair[K, V](key: K, value: V) -> tuple[K, V]: return key, value # Generic TypeAlias (Python 3.12+) type Point[T] = tuple[T, T] int_point: Point[int] = (1, 2)
Аннотации в виде Строк (Forward References)
- Если тип еще не определен (например, циклическая зависимость или тип определяется позже в файле), его можно указать в виде строки.
class Employee: def __init__(self, name: str, manager: 'Employee | None'): # 'Employee' - forward reference self.name = name self.manager = manager
from __future__ import annotations
(рекомендуется): Автоматически обрабатывает все аннотации как строки, избавляя от необходимости вручную их оборачивать в кавычки. Помещается в начале файла.
Инструменты Статического Анализа
- MyPy: Наиболее популярный.
- Pyright (от Microsoft, используется в Pylance для VS Code).
- Pytype (от Google).
- Они проверяют код на соответствие аннотациям до его запуска.
〰〰〰 𓆝 𓆟 𓆞 𓆝 𓆟 𓆝 𓆟 𓆞 〰〰〰