Основы

  • Что это? Подсказки (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

Оператор Объединения (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=Falseclass 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).
  • Они проверяют код на соответствие аннотациям до его запуска.

〰〰〰 𓆝 𓆟 𓆞 𓆝 𓆟 𓆝 𓆟 𓆞 〰〰〰