Что такое рекурсия
Рекурсивная функция — это функция, которая вызывает саму себя. У каждой такой функции должно быть:
- Базовый случай — условие, при котором рекурсия останавливается и возвращается результат без нового вызова.
- Рекурсивный шаг — вызов функции с меньшей или более простой частью данных.
Например, сумма списка чисел: базовый случай — пустой список (сумма 0), рекурсивный шаг — первый элемент плюс сумма оставшегося хвоста.
def recursive_sum(numbers: list) -> float:
"""Return the sum of all numbers in a list recursively."""
if not isinstance(numbers, list):
raise TypeError("Argument must be a list")
if not numbers:
return 0
head = numbers[0]
if not isinstance(head, (int, float)):
raise TypeError(f"All elements must be numbers, got {type(head).__name__}")
return head + recursive_sum(numbers[1:])
numbers = [1, 2, 3, 4, 5]
try:
print(recursive_sum(numbers))
except (TypeError, ValueError) as e:
print(f"Ошибка: {e}")
Каждый рекурсивный вызов добавляет кадр в стек вызовов. Если забыть базовый случай или передать слишком глубокую структуру, Python остановит программу с RecursionError.
Валидация через guard clauses
Guard clause — это проверка в начале функции, которая немедленно прерывает выполнение при нарушении условий. Вместо вложенных if/else мы пишем:
def list_depth(nested_list: list) -> int:
if not isinstance(nested_list, list):
raise TypeError("Argument must be a list")
...
Такой код проще читать: сначала мы отсекаем всё некорректное, а затем решаем задачу.
isinstance вместо type()
Для проверки типа предпочтительнее isinstance(obj, SomeType), а не type(obj) == SomeType. isinstance учитывает наследование и позволяет проверять несколько типов сразу:
isinstance(42, (int, float)) # True
isinstance([1, 2], list) # True
isinstance({}, (list, dict)) # True
isinstance вместо type(), guard clauses и type hints выходит за рамки исходного практикума, но является рекомендуемой современной практикой Python.
Обработка исключений при вызове
Функция сообщает о проблеме через raise. Вызывающий код решает, что делать дальше: вывести сообщение, залогировать или прекратить работу. В практикуме каждый вызов оборачивается в try/except, чтобы продемонстрировать корректную обработку.
try:
print(reverse_string(123)) # не строка
except TypeError as e:
print(f"Ошибка: {e}")
Краткий итог
Рекурсия + guard clauses + try/except — устойчивая комбинация. Мы сначала проверяем данные, затем решаем задачу рекурсивно, а внешний код корректно реагирует на ошибки.