📖 Теория: Summary session 12

⚡ Кратко

Docstrings — строки документации в тройных кавычках, доступные через help().

Аннотации типов указывают ожидаемые типы, но не влияют на выполнение кода.

Рекурсия требует базового случая и движения к нему; хвостовая рекурсия в Python не оптимизируется.

deepcopy() рекурсивно создаёт независимую копию вложенных коллекций.

Документация и docstrings

Документация — это описание кода, которое помогает разработчикам понимать, как работают функции и другие компоненты программы. В Python документация функции обычно записывается в виде docstring — строки в тройных кавычках, расположенной сразу после заголовка функции.

# docstring_demo.py
def greet(name: str) -> str:
    """Возвращает приветственное сообщение для пользователя.

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


print(greet("Alice"))
print(help(greet))

В отличие от обычных комментариев #, docstrings сохраняются как атрибут __doc__ и доступны через help().

Функция help()

help(object) выводит документацию объекта: его docstring, параметры и другие сведения. Если вызвать help() без аргументов, откроется интерактивный справочный режим.

# help_demo.py
help(sum)
help(str)
help(greet)

Аннотации типов

Аннотации типов — это механизм, позволяющий указывать ожидаемые типы аргументов и возвращаемого значения функции, а также типы переменных.

# type_hints_demo.py
def add(a: int, b: int) -> int:
    return a + b


num: int = 10
print(add(3, 5))
print(add("3", "5"))  # не вызовет ошибку во время выполнения
💡 На заметку: аннотации не влияют на выполнение кода. Они нужны для читаемости, подсказок IDE и статического анализа (например, с помощью mypy).

Аннотации для коллекций

В Python 3.9+ для списков, кортежей, множеств и словарей можно использовать встроенные типы с указанием содержимого:

# collection_type_hints.py
def process_numbers(numbers: list[int]) -> list[int]:
    """Возвращает список квадратов чисел."""
    return [n ** 2 for n in numbers]


def unique_chars(text: str) -> set[str]:
    """Возвращает множество уникальных символов."""
    return set(text)


def count_words(text: str) -> dict[str, int]:
    """Считает частоту каждого слова в строке."""
    words = text.split()
    return {word: words.count(word) for word in set(words)}
⚠️ Проверить по документации: в Python до 3.9 для аннотации коллекций использовались классы из модуля typing: List[int], Dict[str, int] и т.д. В современном коде рекомендуется встроенный синтаксис list[int].

Работа со словарями

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

# dict_transform.py
original_dict = {"a": 1, "b": 2, "c": 3}
inverted_dict = {value: key for key, value in original_dict.items()}
print(inverted_dict)


number_to_word = {1: "один", 2: "два", 3: "три"}
data = {"x": 1, "y": 2, "z": 3}
translated = {
    key: number_to_word.get(value, value)
    for key, value in data.items()
}
print(translated)

Рекурсия: базовый и рекурсивный случай

Рекурсия — это когда функция вызывает сама себя для решения уменьшенной версии задачи. Любая корректная рекурсия состоит из двух частей:

  • Базовый случай — условие остановки.
  • Рекурсивный случай — вызов себя с более простыми данными.
# factorial.py
def factorial(n: int) -> int:
    """Возвращает факториал неотрицательного целого числа."""
    if n == 0 or n == 1:  # базовый случай
        return 1
    return n * factorial(n - 1)  # рекурсивный случай


print(factorial(5))  # 120

Хвостовая рекурсия

Хвостовая рекурсия — частный случай, при котором рекурсивный вызов является последней операцией функции. В некоторых языках такой вызов оптимизируется, но в Python это не работает.

# tail_recursion.py
def factorial_tail(n: int, accumulator: int = 1) -> int:
    if n == 0 or n == 1:
        return accumulator
    return factorial_tail(n - 1, n * accumulator)


print(factorial_tail(5))  # 120

Для глубоких вычислений используйте итерацию:

# iterative_factorial.py
def factorial_iterative(n: int) -> int:
    accumulator = 1
    while n > 1:
        accumulator *= n
        n -= 1
    return accumulator


print(factorial_iterative(5))  # 120

Рекурсия или итерация?

КритерийРекурсияИтерация
ЧитаемостьВысокая для деревьев, графов, вложенных структурВыше для простых последовательностей
ПамятьСтек вызовов растёт с глубинойОбычно меньше
СкоростьМедленнее из-за накладных расходовБыстрее
РискRecursionErrorБесконечный цикл
Когда использоватьДеревья, графы, вложенные данные, разбор выраженийПростые переборы, большие глубины

Глубокое копирование

Функция deepcopy() из модуля copy использует рекурсию, чтобы создать полностью независимую копию объекта. Обычный copy() копирует только верхний уровень.

# deepcopy_demo.py
from copy import deepcopy


original_list = [[1, 2], [3, 4]]
copy_lst = deepcopy(original_list)
copy_lst[0][0] = "X"

print("Оригинал:", original_list)  # [[1, 2], [3, 4]]
print("Копия:", copy_lst)          # [['X', 2], [3, 4]]
⚠️ Проверить по документации: детали поведения deepcopy() для циклических ссылок, пользовательских классов и слабых ссылок сверяйте с официальной документацией модуля copy.