Встречается на 35% собеседований по Python

Что такое декоратор в Python

Декоратор в Python — это функция, которая принимает другую функцию (или метод) в качестве аргумента и возвращает новую функцию, расширяющую или изменяющую поведение исходной, не изменяя её код. Декораторы применяются для логирования, замера времени выполнения, проверки прав доступа, кэширования и других сквозных задач.

Как это работает

Декоратор — это синтаксический сахар над присваиванием. Когда вы пишете:

python
@my_decorator
def func():
    pass

это эквивалентно:

python
def func():
    pass
func = my_decorator(func)

Декоратор обычно определяется как функция, которая принимает функцию и возвращает обёртку (wrapper). Внутри обёртки можно выполнять действия до и после вызова исходной функции.

Пример

Простейший декоратор для логирования вызовов:

python
import functools

def log_calls(func):
    @functools.wraps(func)  # сохраняет имя и docstring исходной функции
    def wrapper(*args, **kwargs):
        print(f"Вызов {func.__name__} с аргументами {args} {kwargs}")
        result = func(*args, **kwargs)
        print(f"Результат: {result}")
        return result
    return wrapper

@log_calls
def add(a, b):
"""Складывает два числа."""
return a + b

add(2, 3)

Вызов add с аргументами (2, 3) {}

Результат: 5

text

Здесь `log_calls` — декоратор, `wrapper` — обёртка, которая логирует вызов и результат. `functools.wraps` копирует метаданные исходной функции в обёртку, иначе `add.__name__` стал бы `'wrapper'`.

## Подводные камни

- **Потеря метаданных**: без `functools.wraps` имя и docstring функции заменяются на данные обёртки, что ломает интроспекцию и документацию.
- **Декораторы с аргументами**: если нужно передать параметры декоратору (например, уровень логирования), требуется дополнительный уровень вложенности:

  ```python
  def repeat(times):
      def decorator(func):
          @functools.wraps(func)
          def wrapper(*args, **kwargs):
              for _ in range(times):
                  result = func(*args, **kwargs)
              return result
          return wrapper
      return decorator
  • Порядок применения: декораторы применяются снизу вверх. Если написать @a над @b, то сначала применится b, затем a.
  • Декораторы для методов: при декорировании методов класса обёртка должна учитывать self — он передаётся как первый аргумент, поэтому используйте *args, **kwargs.

Когда использовать

Декораторы оправданы, когда нужно:

  • Логировать вызовы функций или их аргументы.
  • Замерять время выполнения (например, для профилирования).
  • Ограничивать доступ (проверка прав пользователя).
  • Кэшировать результаты (например, functools.lru_cache).
  • Реализовать паттерн "цепочка обязанностей" или "наблюдатель".

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

Коротко

  • Декоратор — функция, принимающая функцию и возвращающая новую функцию, расширяющую поведение исходной.
  • Синтаксис @decorator — это присваивание func = decorator(func).
  • Используйте functools.wraps, чтобы сохранить метаданные исходной функции.
  • Декораторы с аргументами требуют тройной вложенности: внешняя функция принимает аргументы, средняя — декорируемую функцию, внутренняя — обёртку.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы