📦 Репозиторий занятия 44

Урок 44. Документация. Аннотации типов

Как работать с репозиторием

Каждая карточка говорит, о чём файл, что он выводит и что в нём искать. Код виден прямо здесь: его можно скопировать одной кнопкой, скачать файл или открыть его целиком.

Маршрут изучения

  1. Прочитайте описание: по нему уже понятно, о чём файл и что он выведет.
  2. Предскажите вывод: сравните своё предположение со строкой «Что выводит».
  3. Запустите: скачайте файл или скопируйте код кнопкой и выполните его у себя.
  4. Измените: поменяйте одно условие или значение и объясните новый результат.

Файлы: рекомендуемый порядок

1
PythonИсполняемый пример36 строк

Docstring и функция help()

less_23__annotation/theory_01__docstring.py

Показывает, что docstring — это строка документации внутри функции, которую можно прочитать через встроенную функцию help(). Первый пример — my_function() с однострочным docstring, второй — help() без аргументов, запускающая интерактивную справочную оболочку Python. Третий пример — функция f(x, y) с docstring в reST-стиле (:param, :type, :return, :rtype), которая только определена и не вызывается.

  • my_function() — однострочный docstring, вызывается help(my_function)
  • help(my_function) — печатает docstring функции
  • help() без аргументов — запускает интерактивную справку Python
  • f(x, y) — docstring в стиле reST: :param, :type, :return, :rtype
  • f(x, y) определена, но нигде не вызывается

Что выводит: Сначала блок help(my_function): 'Help on function my_function in module __main__' и текст docstring. Затем приветствие интерактивной справки ('Welcome to Python's help utility!...') и подсказка 'help> '; при отсутствии ввода (EOF) справка сразу завершается сообщением 'You are now leaving help and returning to the Python interpreter.'

Начало файла
"""
Docstring - простой и удобный способ добавить детальное описание в функцию.
(что-то вроде инструкции пользователя.

Извлечь можно с помощью функции help().
"""


def my_function():
    """
    This is my function: very simple and very useful and very good way to add a description.
    Return nothing.

    :return: None
Показать файл целиком (36 строк)
"""
Docstring - простой и удобный способ добавить детальное описание в функцию.
(что-то вроде инструкции пользователя.

Извлечь можно с помощью функции help().
"""


def my_function():
    """
    This is my function: very simple and very useful and very good way to add a description.
    Return nothing.

    :return: None
    """
    print("Hello World")


help(my_function)
help()

def f(x, y):
    """
    Adds two numbers and returns the result.

    This function takes two numerical inputs and adds them together. It
    is expected that both parameters are valid numbers that can be added.
    The result of the addition will be returned.

    :param x: The first number to be added.
    :type x: int or float
    :param y: The second number to be added.
    :type y: int or float
    :return: The sum of the two numbers.
    :rtype: int or float
    """
Проверьте себя: Что произойдёт при запуске help() без аргументов в обычной интерактивной консоли Python — и почему в неинтерактивном запуске (например, при перенаправлении stdin) справка сразу закрывается?
Открыть файл →
2
MarkdownРазбор концепции66 строк

Аннотации типов: что это и зачем

less_23__annotation/theory_02__annotation.md

Объясняет, что аннотации типов в Python — это подсказки для разработчиков, IDE и статических анализаторов (mypy, pyright, pylance), а не инструкции, которые проверяет сам интерпретатор: ошибка в аннотации не остановит выполнение кода. Перечисляет практическую пользу — читаемость, самодокументирование, поддержка автодополнения и интеграция с FastAPI/Pydantic. Отдельная таблица сравнивает старый синтаксис через typing (List[int]) с современным встроенным (list[int]) после Python 3.9.

  • greeting(name: str, age: int) -> str — базовый пример аннотации параметров и возврата
  • 4 причины использовать аннотации: читаемость, IDE, самодокументирование, интеграция с FastAPI/Pydantic
  • Таблица: List[int]/Dict[str,int]/Tuple[...] (до 3.9) против list[int]/dict[str,int]/tuple[...] (после 3.9)
  • Optional[int] и Union[int, str] (typing) против int | None и int | str (после 3.10)
  • Универсальное правило: то, что даёт ошибку без импорта — импортировать из typing
Показать начало файла (66 строк всего)
## Что такое аннотации в Python?

**Аннотации** — это специальный синтаксис в Python, позволяющий указывать типы данных.

Они не влияют напрямую на выполнение программы, а служат **подсказками** для разработчиков,  
IDE и систем статического анализа (например, `mypy`, `pyright`, `pylance`).

Пример:

```python
def greeting(name: str, age: int) -> str:
    return f"Hi, {name}! Your age is {age}."
```

Здесь:

* `name: str` — аннотации типов аргументов `name` и `age`.
* `-> str` — аннотация возвращаемого значения.

---

## Зачем нужны аннотации?

Аннотации не влияют на выполнение кода. Поэтому ошибку в аннотации Python "не заметит".  
(Если, конечно, это не синтаксическая ошибка).

1. Улучшают читаемость кода

2. Помогают IDE в проверке и раннем предупреждении ошибок

3. Само-документирование кода
   * аннотации выполняют роль документации, не требуя отдельного описания типов в docstring.

4. **Интеграция с автодополнением и статическим анализом**
   IDE (PyCharm, VS Code) и системы автогенерации API (например, FastAPI, Pydantic) используют аннотации для точных подсказок и валидации.

---

## Таблица сравнения аннотаций 


| **Тип данных**              | **Аннотация до 3.9 (`typing`)** | **Аннотация после 3.9 (встроенная)** |
|-----------------------------| ------------------------------- |--------------------------------------|
| Список                      | `List[int]`                     | `list[int]`                          |
| Словарь                     | `Dict[str, int]`                | `dict[str, int]`                     |
| Тюпл                        | `Tuple[int, str]`               | `tuple[int, str]`                    |
| Множество                   | `Set[str]`                      | `set[str]`                           |
| Неизменяемое множество      | `FrozenSet[int]`                | `frozenset[int]`                     |
| Итератор                    | `Iterator[int]`                 | `Iterator[int]`                      |
| Генератор                   | `Generator[int, None, None]`    | `Generator[int, None, None]`         |
| Опциональное значение       | `Optional[int]`                 | `int \| None`                        |
| Объединение типов           | `Union[int, str]`               | `int \| str`                         |
| Любой тип                   | `Any`                           | `Any`                                |
| Вызываемый объект (функция) | `Callable[[int], str]`          | `Callable[[int], str]`               |
| None                        | `None`                          | `None`                               |


## УНИВЕРСАЛЬНОЕ ПРАВИЛО:

Всё, что при аннотации даёт ошибку, следует импортировать из модуля `typing`
…
Проверьте себя: Почему ошибка в аннотации типа не приводит к падению программы при запуске, и какие инструменты всё же её замечают?
3
MarkdownРазбор концепции56 строк

Синтаксис аннотаций: базовые типы, коллекции, Optional/Union

less_23__annotation/theory_03__annotation_syntax.md

Каталог конкретного синтаксиса аннотаций: простые переменные (str, int, float, bool, bytes), коллекции (list[int], set[str], frozenset[int], dict[str, int], tuple[str, int]) и вложенные структуры вроде dict[str, list[tuple[str, int]]]. Отдельный блок — старый (typing.Optional/Union) и новый (int | None, int | str) синтаксис для опциональных и объединённых типов, доступный с Python 3.10.

  • Базовые типы: name: str, age: int, price: float, active: bool, data: bytes
  • Коллекции: numbers: list[int], names: set[str], user_ids: frozenset[int], options: dict[str, int], pairs: tuple[str, int]
  • До 3.9 те же коллекции писались через typing: List[int], Dict[str, int], Tuple[str, int]
  • Вложенные структуры: matrix: list[list[int]], config: dict[str, list[tuple[str, int]]]
  • Optional/Union до 3.10 (typing.Optional[int], typing.Union[int, str]) против 3.10+ (int | None, int | str)
Показать начало файла (56 строк всего)
## 1. Базовые типы

```python
name: str
age: int
price: float
active: bool
data: bytes
```

---

## 2. Коллекции

```python
numbers: list[int]
names: set[str]
user_ids: frozenset[int]
options: dict[str, int]
pairs: tuple[str, int]
```

> 🔹 До Python 3.9 приходилось писать `List[int]`, `Dict[str, int]`, `Tuple[str, int]` через модуль `typing`.

---

## 3. Вложенные структуры

```python
matrix: list[list[int]]
config: dict[str, list[tuple[str, int]]]
```

ВАЖНО: Вложенные структуры усложняют читаемость кода.  
Поэтому указывать вложенный тип желательно, но совсем не обязательно

---

## 4. Опциональные и объединённые типы (Python 3.10+)

### Старый синтаксис (до 3.10):

```python
from typing import Optional, Union

id: Optional[int]  # то же самое, что Union[int, None]
value: Union[int, str]
```

### Новый синтаксис (3.10+):

```python
id: int | None
value: int | str
```
Проверьте себя: Чем id: Optional[int] отличается от id: int | None по смыслу и с какой версии Python доступен второй вариант?
4
PythonИсполняемый пример40 строк

Мутация списка внутри функции vs копия

less_23__annotation/theory_04__immutable_parameters.py

Демонстрирует главную ловушку изменяемых типов: func(lst) добавляет элемент через lst.append(4) — это меняет тот же объект, что и снаружи функции, поэтому result и исходный lst совпадают (result == lst даёт True). Второй вариант modified_func делает поверхностную копию new_lst = lst[:] перед изменением, поэтому исходный список снаружи остаётся нетронутым.

  • func(lst) — lst.append(4) мутирует переданный список напрямую
  • result = func(lst); print(result == lst) — True, это один и тот же объект
  • modified_func(lst) — new_lst = lst[:] делает копию перед append(4)
  • result = modified_func(lst); print(result == lst) — False, исходный список не изменился

Что выводит: [1, 2, 3, 4] / [1, 2, 3, 4] / True — для func (список мутирован и снаружи); затем [1, 2, 3, 4] / [1, 2, 3] / False — для modified_func (исходный список не тронут).

Начало файла
"""
Изменение изменяемых типов данных внутри функции изменит их и снаружи.
Поэтому перед изменением нужно делать их копию.
Глубокую или поверхностную - зависит от типа данных.
"""


def func(lst: list[int]) -> list:
    lst.append(4)
    return lst


lst = [1, 2, 3]
Показать файл целиком (40 строк)
"""
Изменение изменяемых типов данных внутри функции изменит их и снаружи.
Поэтому перед изменением нужно делать их копию.
Глубокую или поверхностную - зависит от типа данных.
"""


def func(lst: list[int]) -> list:
    lst.append(4)
    return lst


lst = [1, 2, 3]

result = func(lst)
print(result)           # [1, 2, 3, 4]
print(lst)              # [1, 2, 3, 4]
print(result == lst)    # True


"""
Это плохой вариант.

===============================================================================

Поэтому функцию func придётся доработать:
"""

def modified_func(lst: list[int]) -> list:
    new_lst = lst[:]
    new_lst.append(4)
    return new_lst


lst = [1, 2, 3]

result = modified_func(lst)
print(result)           # [1, 2, 3, 4]
print(lst)              # [1, 2, 3]
print(result == lst)    # False
Проверьте себя: Почему result == lst даёт True в первом случае и False во втором, хотя оба раза result выглядит как [1, 2, 3, 4]?
Открыть файл →