Встречается на 16% собеседований по Data Engineer

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

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

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

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

Пример простейшего декоратора:

python
def my_decorator(func):
    def wrapper():
        print("До вызова функции")
        func()
        print("После вызова функции")
    return wrapper

@my_decorator
def say_hello():
    print("Привет!")

say_hello()

Вывод:

text
До вызова функции
Привет!
После вызова функции

Синтаксис @my_decorator эквивалентен say_hello = my_decorator(say_hello). То есть декоратор применяется к функции сразу после её определения.

Примеры использования

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

Логирование вызовов:

python
def log(func):
    def wrapper(*args, **kwargs):
        print(f"Вызов {func.__name__} с аргументами {args}")
        return func(*args, **kwargs)
    return wrapper

@log
def add(a, b):
    return a + b

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

Кэширование результатов:

python
def cache(func):
    memo = {}
    def wrapper(n):
        if n not in memo:
            memo[n] = func(n)
        return memo[n]
    return wrapper

@cache
def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

Проверка прав доступа:

python
def require_admin(func):
    def wrapper(user, *args, **kwargs):
        if not user.is_admin:
            raise PermissionError("Требуются права администратора")
        return func(user, *args, **kwargs)
    return wrapper

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

При использовании декораторов важно помнить о нескольких нюансах.

  • Потеря метаданных функции. Обычный декоратор заменяет исходную функцию на wrapper, из-за чего теряются __name__, __doc__ и другие атрибуты. Решение — использовать functools.wraps:

    python
    from functools import wraps
    
    def my_decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
  • Декораторы с аргументами. Если нужно передать декоратору параметры (например, уровень логирования), требуется дополнительный уровень вложенности:

    python
    def repeat(times):
        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                for _ in range(times):
                    result = func(*args, **kwargs)
                return result
            return wrapper
        return decorator
    
    @repeat(3)
    def say_hi():
        print("Привет")
  • Порядок применения. Несколько декораторов применяются снизу вверх, а выполняются сверху вниз. Например, @a и @b эквивалентны a(b(func)).

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

Декораторы стоит применять, когда нужно единообразно добавить поведение к множеству функций без дублирования кода. Они особенно полезны в веб-фреймворках (например, для маршрутизации), в ORM (для связей), в тестировании (для моков) и в аспектно-ориентированном программировании. Однако не стоит злоупотреблять: излишняя вложенность декораторов может усложнить чтение и отладку кода.

Коротко

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