Получить доступ

Как написать декоратор с параметрами и зачем нужен functools.wraps?

MiddleФункции
Ещё спрашивают так:
  • Почему после декоратора у функции другое имя и пропала документация?
Коротко

Декоратор с параметрами — фабрика декораторов: функция принимает параметры и возвращает обычный декоратор, т. е. три уровня вложенности. functools.wraps копирует в обёртку __name__, __doc__, __module__, __qualname__ и ставит __wrapped__ — иначе логи, документация, pytest и фреймворки видят «wrapper».

  • @retry(times=3) ≡ func = retry(times=3)(func)
  • __wrapped__ позволяет добраться до исходной функции (inspect.signature его учитывает)
  • Для async-функций обёртка тоже должна быть async

А ещё Декоратор-класс — когда нужно состояние (счётчик, кэш)

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Уровни: retry(times=5) возвращает decorator; decorator(fetch) возвращает wrapper; wrapper выполняется при каждом вызове. Параметры times и delay доступны в обёртке через замыкание.

Без wraps: fetch.__name__ == "wrapper", docstring потерян, FastAPI и Click, читающие сигнатуру и имя, работают неправильно, в логах и профилях все функции называются одинаково.

Декоратор, работающий и со скобками, и без: проверяют, передан ли первым аргументом вызываемый объект — но это усложнение; проще всегда писать со скобками.

Async: для корутин нужна async def wrapper с await func(...); универсальный декоратор проверяет inspect.iscoroutinefunction(func).

Пример

import functools, time

def retry(times=3, delay=0.5):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except Exception:
                    if attempt == times:
                        raise
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(times=5)
def fetch(url): ...

Что хотят услышать

  • Три уровня и роль замыкания.
  • Что делает wraps и зачем.
  • Нюанс с async-функциями.

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем