Назад к подготовке

Декораторы и генераторы в Python

Что такое декоратор, зачем он нужен, и почему код внутри генератора выполняется не при создании, а при итерации?

Ответить самому

Сначала сформулируйте ответ как на собеседовании, затем откройте разбор и оцените себя.

Загрузка

Короткий ответ

Декоратор принимает функцию и возвращает новую функцию-обертку. Генераторная функция при вызове возвращает generator object; ее тело начинает выполняться только при next(), цикле или list(generator).

Полный разбор

Декоратор - это обычная функция высшего порядка. Синтаксис @decorator примерно равен func = decorator(func). Обертка может логировать вызовы, проверять права, кешировать результат, менять аргументы или выполнять код до/после исходной функции. Для сохранения имени и docstring обычно используют functools.wraps.

Генераторная функция содержит yield. При вызове она не выполняет тело сразу, а возвращает generator object. Код внутри начнет выполняться, когда кто-то запросит следующий элемент: next(g), for, list(g). После каждого yield состояние функции сохраняется до следующего шага.

Поэтому принты до/после yield появляются в момент итерации, а не в момент создания генератора. Это часто спрашивают, чтобы проверить понимание lazy evaluation.

Теория

Декораторы меняют вызываемый объект, генераторы лениво производят значения и сохраняют состояние между итерациями.

Типичные ошибки

  • Думать, что генератор выполняется при вызове функции.
  • Писать декоратор без `*args, **kwargs` и ломать сигнатуру вызова.
  • Забывать `functools.wraps` в production-декораторах.