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

Что такое декоратор и как его написать?

JuniorФункции
Ещё спрашивают так:
  • Как работают декораторы в Python?
  • Напишите декоратор, который замеряет время выполнения функции
  • Зачем нужен functools.wraps?
Коротко

Декоратор — функция, которая принимает функцию и возвращает новую; @dec — сахар для f = dec(f).

  • Обёртка внутри — замыкание, вызывает исходную через *args, **kwargs
  • functools.wraps сохраняет имя и docstring исходной функции

А ещё Покажите декоратор с параметрами — это три уровня вложенности

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

Подробно

Функции в Python — объекты: их можно передавать и возвращать. Декоратор этим пользуется: получает функцию, определяет внутри обёртку, которая что-то делает до и после вызова, и возвращает обёртку.

Запись

@timer
def work(): ...

полностью эквивалентна work = timer(work) и выполняется один раз — при определении функции.

Обёртка — замыкание: она помнит исходную функцию из внешней области. Чтобы подходить к любой функции, обёртка принимает *args, **kwargs и передаёт их дальше, а результат возвращает.

Без @functools.wraps(func) у обёртки будет имя wrapper и пустой docstring — это ломает логи, отладку и документацию.

Декоратор с параметрами (@retry(3)) — это функция, которая возвращает декоратор: снаружи параметры, внутри декоратор, ещё глубже обёртка.

Примеры из жизни: @property, @staticmethod, @functools.lru_cache, маршруты во Flask и FastAPI (@app.get("/")), проверка прав, логирование, повторные попытки.

Пример

import functools
import time

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        try:
            return func(*args, **kwargs)
        finally:
            print(f"{func.__name__}: {time.perf_counter() - start:.3f} с")
    return wrapper

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

@timer
@retry(3)
def fetch():
    ...

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

  • Определение через «функция принимает и возвращает функцию».
  • Эквивалентность @dec и f = dec(f), момент применения.
  • Рабочий код обёртки с *args, **kwargs и возвратом результата.
  • functools.wraps и декоратор с параметрами.

Частые ошибки

  • Забыть return результата исходной функции в обёртке.
  • Вызвать функцию при декорировании: return wrapper() вместо return wrapper.
  • Не знать, что порядок нескольких декораторов — снизу вверх.

Что спросят дальше

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

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

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

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

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