Перейти к содержанию
  • Лента
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
Свернуть
exlends
Категории
  1. Главная
  2. Категории
  3. Языки программирования
  4. Python
  5. Аннотации типов в Python — полный гайд с примерами

Аннотации типов в Python — полный гайд с примерами

Запланировано Прикреплена Закрыта Перенесена Python
1 Сообщения 1 Постеры 13 Просмотры
  • Сначала старые
  • Сначала новые
  • По количеству голосов
Ответить
  • Ответить, создав новую тему
Авторизуйтесь, чтобы ответить
Эта тема была удалена. Только пользователи с правом управления темами могут её видеть.
  • AladdinA Не в сети
    AladdinA Не в сети
    Aladdin
    js
    написал отредактировано Aladdin
    #1

    Python динамически типизирован: интерпретатор не проверяет типы во время выполнения. Аннотации типов — это подсказки (type hints) для инструментов статического анализа, IDE и разработчиков.

    Три главных эффекта:

    • Читаемость — сразу видно, что принимает и возвращает функция
    • Статический анализ — mypy, pyright находят ошибки до запуска
    • Автодополнение в IDE — Zed, VS Code, PyCharm знают типы и предлагают правильные методы

    Аннотации не влияют на скорость выполнения и не бросают исключений сами по себе. Проверку можно включить явно через runtime-библиотеки.


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

    Переменные

    name: str = "Alice"
    age: int = 30
    pi: float = 3.14
    is_active: bool = True
    nothing: None = None
    
    # Объявление без инициализации (для классов, модульного уровня)
    user_id: int  # значения ещё нет, но тип зафиксирован
    

    Функции

    def greet(name: str) -> str:
        return f"Hello, {name}"
    
    def add(a: int, b: int) -> int:
        return a + b
    
    def log(message: str) -> None:  # ничего не возвращает
        print(message)
    

    Синтаксис: после имени параметра ставится : тип, после скобок -> тип для возвращаемого значения.


    Встроенные коллекции (Python 3.9+)

    С Python 3.9 можно использовать встроенные типы напрямую, без импорта из typing:

    def process(items: list[int]) -> dict[str, int]:
        return {str(i): i for i in items}
    
    def lookup(mapping: dict[str, list[float]]) -> tuple[str, ...]:
        return tuple(mapping.keys())
    
    coords: set[int] = {1, 2, 3}
    pair: tuple[int, str] = (42, "hello")
    

    До Python 3.9 нужно было писать List[int], Dict[str, int] из модуля typing.


    Модуль typing — специальные конструкции

    Optional — значение или None

    from typing import Optional
    
    def find_user(user_id: int) -> Optional[str]:
        if user_id == 1:
            return "Alice"
        return None
    

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

    def find_user(user_id: int) -> str | None:  # Python 3.10+
        ...
    

    Union — один из нескольких типов

    from typing import Union
    
    def stringify(value: Union[int, float, str]) -> str:
        return str(value)
    
    # Python 3.10+
    def stringify(value: int | float | str) -> str:
        return str(value)
    

    Any — отключение проверки

    from typing import Any
    
    def debug(value: Any) -> None:
        print(value)
    

    Any совместим с любым типом в обе стороны. Используется, когда тип действительно неизвестен или для постепенной миграции.

    Literal — конкретные допустимые значения

    from typing import Literal
    
    def set_direction(direction: Literal["left", "right", "up", "down"]) -> None:
        ...
    
    def get_status() -> Literal[0, 1, -1]:
        return 0
    

    Final — константы

    from typing import Final
    
    MAX_SIZE: Final = 100
    API_URL: Final[str] = "https://api.example.com"
    

    Переменная, помеченная Final, не должна переопределяться.


    Callable и функции как аргументы

    from typing import Callable
    
    # Функция принимает int, возвращает str
    def apply(func: Callable[[int], str], value: int) -> str:
        return func(value)
    
    # Функция без аргументов, возвращающая None
    Handler = Callable[[], None]
    
    def register(callback: Handler) -> None:
        callback()
    

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

    TypeVar нужен, когда тип возврата зависит от типа аргумента:

    from typing import TypeVar
    
    T = TypeVar("T")
    
    def first(items: list[T]) -> T:
        return items[^0]
    
    result_int: int = first([1, 2, 3])      # T = int
    result_str: str = first(["a", "b"])     # T = str
    

    TypeVar с ограничениями:

    from typing import TypeVar
    
    Number = TypeVar("Number", int, float)
    
    def double(x: Number) -> Number:
        return x * 2
    

    Python 3.12 — новый синтаксис дженериков

    # Python 3.12+, не нужно объявлять TypeVar явно
    def first[T](items: list[T]) -> T:
        return items[^0]
    
    def zip_with[T, U](a: list[T], b: list[U]) -> list[tuple[T, U]]:
        return list(zip(a, b))
    

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

    class User:
        name: str
        age: int
        email: str | None = None  # поле со значением по умолчанию
    
        def __init__(self, name: str, age: int) -> None:
            self.name = name
            self.age = age
    
        def greet(self) -> str:
            return f"Hi, I'm {self.name}"
    

    ClassVar — атрибуты класса, не экземпляра

    from typing import ClassVar
    
    class Config:
        debug: ClassVar[bool] = False
        max_retries: ClassVar[int] = 3
        timeout: float = 30.0  # атрибут экземпляра
    

    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']})")
    
    film: Movie = {"title": "Inception", "year": 2010, "rating": 8.8}
    

    С необязательными ключами:

    from typing import TypedDict, NotRequired
    
    class Config(TypedDict):
        host: str
        port: int
        debug: NotRequired[bool]  # Python 3.11+
    

    Protocol — структурная типизация (duck typing)

    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, хотя Circle не наследует Drawable
    render(Square())  # OK
    

    dataclass + аннотации

    from dataclasses import dataclass, field
    
    @dataclass
    class Point:
        x: float
        y: float
        label: str = "default"
        tags: list[str] = field(default_factory=list)
    
    p = Point(1.0, 2.5)
    

    @dataclass читает аннотации классовых полей и автоматически генерирует __init__, __repr__, __eq__.


    overload — перегрузка сигнатур

    from typing import overload
    
    @overload
    def process(value: int) -> str: ...
    @overload
    def process(value: str) -> int: ...
    
    def process(value: int | str) -> str | int:
        if isinstance(value, int):
            return str(value)
        return len(value)
    

    @overload позволяет статическому анализатору понять, какой тип вернётся при каком входе.


    TYPE_CHECKING — избегаем циклических импортов

    from __future__ import annotations
    from typing import TYPE_CHECKING
    
    if TYPE_CHECKING:
        from mymodule import HeavyClass  # импортируется только при статическом анализе
    
    def process(obj: "HeavyClass") -> None:
        ...
    

    from __future__ import annotations (PEP 563) делает все аннотации строками-ленивыми вычислениями, что полностью решает проблему forward references.


    ParamSpec и Concatenate — аннотации для декораторов

    from typing import ParamSpec, TypeVar, Callable
    import functools
    
    P = ParamSpec("P")
    R = TypeVar("R")
    
    def logged(func: Callable[P, R]) -> Callable[P, R]:
        @functools.wraps(func)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
            print(f"Calling {func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    
    @logged
    def add(a: int, b: int) -> int:
        return a + b
    

    ParamSpec сохраняет полную сигнатуру оборачиваемой функции — IDE будет знать типы аргументов декорированной функции.


    Self — тип текущего класса

    from typing import Self  # Python 3.11+
    
    class Builder:
        def set_name(self, name: str) -> Self:
            self.name = name
            return self
    
        def set_age(self, age: int) -> Self:
            self.age = age
            return self
    
    b = Builder().set_name("Alice").set_age(30)
    

    Используйте Self вместо имени класса в строке, чтобы наследники получали правильный тип при цепочечных вызовах.


    Never и NoReturn

    from typing import NoReturn, Never  # Never появился в 3.11
    
    def crash() -> NoReturn:
        raise RuntimeError("Fatal error")
    
    def assert_never(value: Never) -> Never:
        raise AssertionError(f"Unexpected value: {value}")
    

    NoReturn говорит: функция никогда не возвращает управление (бросает исключение или бесконечный цикл). Never — тип-дно, с которым ничего не совместимо.


    Инструменты статического анализа

    Инструмент Установка Запуск
    mypy uv add mypy mypy src/
    pyright uv add pyright pyright
    ruff uv add ruff ruff check . (lint + types)

    Пример конфига mypy.ini:

    [mypy]
    python_version = 3.14
    strict = true
    ignore_missing_imports = true
    

    Флаг --strict включает полный набор проверок, включая disallow_untyped_defs, warn_return_any и другие.


    Быстрая шпаргалка по версиям

    Конструкция Минимальная версия
    list[int], dict[str, int] напрямую 3.9
    X \| Y вместо Union[X, Y] 3.10
    Self, Never, NotRequired 3.11
    def f[T](...) — новый синтаксис дженериков 3.12

    Полезные источники

    • PEP 484 — основной PEP, вводящий аннотации типов: python.org/dev/peps/pep-0484
    • PEP 526 — аннотации переменных: python.org/dev/peps/pep-0526
    • PEP 604 — синтаксис X | Y: python.org/dev/peps/pep-0604
    • PEP 695 — дженерики через [T] в Python 3.12: python.org/dev/peps/pep-0695
    • Документация mypy: mypy.readthedocs.io
    • typing — официальная документация: docs.python.org/3/library/typing.html
    • Pyright: github.com/microsoft/pyright
      ^10^12^14^16^18^20^6^8
    ⁂
    1 ответ Последний ответ
    0

    Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.

    Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.

    С вашими комментариями этот пост мог бы стать ещё лучше 💗

    Зарегистрироваться Войти

    Категории

    • Главная
    • Новости
    • Фронтенд
    • Бекенд
    • Языки программирования

    Контакты

    • Сотрудничество
    • info@exlends.com

    © 2024 - 2026 ExLends, Inc. Все права защищены.

    Политика конфиденциальности
    • Войти

    • Нет учётной записи? Зарегистрироваться

    • Войдите или зарегистрируйтесь для поиска.
    • Первое сообщение
      Последнее сообщение
    0
    • Лента
    • Категории
    • Последние
    • Метки
    • Популярные
    • Пользователи
    • Группы