⚖️ Старый и современный подход

⚡ Различия в двух словах

Старый: для аннотаций коллекций использовались List, Dict и т.д. из модуля typing.

Новый: в Python 3.9+ используются встроенные list[int], dict[str, int], а в Python 3.10+ — оператор | вместо Union.

Вывод: современный синтаксис короче, чище и не требует лишних импортов.

📜 Коллекции: стиль Python <3.9

# old_style.py
from typing import Dict, List, Set, Tuple

def process_numbers(numbers: List[int]) -> List[int]:
    return [n ** 2 for n in numbers]

def get_point() -> Tuple[str, float, float]:
    return "A", 1.5, 2.5

def count_words(text: str) -> Dict[str, int]:
    words = text.split()
    return {word: words.count(word) for word in words}

def unique_chars(text: str) -> Set[str]:
    return set(text)

Что здесь происходит: каждый generic-тип импортируется из модуля typing с заглавной буквы. Это работает во всех версиях Python, начиная с 3.5, но выглядит многословно.

❌ Почему старый подход менее удобен сейчас

  • Лишние импорты увеличивают начало файла.
  • Заглавные буквы List/Dict легко перепутать со встроенными list/dict.
  • Новые разработчики чаще видят современный синтаксис в документации и библиотеках.

✅ Современный вариант (Python 3.9+)

# modern_style.py
def process_numbers(numbers: list[int]) -> list[int]:
    return [n ** 2 for n in numbers]

def get_point() -> tuple[str, float, float]:
    return "A", 1.5, 2.5

def count_words(text: str) -> dict[str, int]:
    words = text.split()
    return {word: words.count(word) for word in words}

def unique_chars(text: str) -> set[str]:
    return set(text)

Что улучшилось: код читает естественнее — тип совпадает с именем встроенного типа, а квадратные скобки указывают параметры типа.

Преимущества:
  • Нет лишних импортов.
  • Меньше визуального шума.
  • Соответствует современной документации Python.

📜 Объединение типов: старый стиль

# old_union.py
from typing import Union

def calculate(value: Union[int, float]) -> float:
    return value ** 2

✅ Современный вариант (Python 3.10+)

# modern_union.py
def calculate(value: int | float) -> float:
    return value ** 2

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

Что улучшилось: оператор | читается как «или», а str | None заменяет Optional[str].

📜 Docstrings: reST/Epytext из исходника

def greet(name: str) -> str:
    """
    Функция принимает имя и возвращает строку приветствия.

    :param name: Имя пользователя.
    :return: Приветственное сообщение.
    """
    return f"Hello, {name}!"

Что здесь происходит: классический формат с полями :param и :return хорошо поддерживается инструментами и IDE.

✅ Альтернативные форматы docstring

⚠️ Проверить по документации: кроме reST-формата, в Python-экосистеме популярны Google Style и NumPy Style docstrings. Они отличаются визуальной разметкой, но содержат те же секции: описание, параметры, возвращаемое значение. Выбор зависит от соглашений команды.

🕰️ Когда старый подход ещё можно встретить

  • Кодовая база, которая поддерживает Python 3.7–3.8.
  • Библиотеки, старающиеся сохранить совместимость со старыми версиями.
  • Legacy-проекты, где миграция на встроенные generic-типы ещё не проведена.

В таких случаях можно постепенно переходить к новому синтаксису или использовать from __future__ import annotations, чтобы писать новый синтаксис даже в старых версиях (строковые аннотации).