Как можно создать сложный декоратор с несколькими уровнями вложенности?

Сложные декораторы с несколькими уровнями вложенности создаются путем возврата функции из функции. Каждый уровень декоратора принимает и возвращает функцию, позволяя настраивать поведение целевой функции на разных этапах. Пример:
    
def outer_decorator(arg1, arg2):
    def middle_decorator(func):
        def inner_wrapper(*args, **kwargs):
            # Логика до вызова функции
            print(f"Внешний декоратор: {arg1}, {arg2}")
            result = func(*args, **kwargs)
            # Логика после вызова функции
            return result
        return inner_wrapper
    return middle_decorator

@outer_decorator("hello", "world")
def my_function(x):
    return x * 2

print(my_function(5))
    
  
В этом примере `outer_decorator` принимает аргументы и возвращает `middle_decorator`. `middle_decorator` принимает декорируемую функцию и возвращает `inner_wrapper`, который, в свою очередь, выполняет код до и после вызова исходной функции.

Создание сложного декоратора с несколькими уровнями вложенности в Python предполагает использование функций, возвращающих функции. Это позволяет создавать декораторы, которые принимают аргументы и/или настраиваются перед тем, как применить их к декорируемой функции. Вот как это можно сделать:


def decorator_factory(*decorator_args, **decorator_kwargs):
    """
    Фабрика декораторов. Принимает аргументы для настройки декоратора.
    Возвращает сам декоратор (функцию, принимающую декорируемую функцию).
    """
    print(f"decorator_factory: args={decorator_args}, kwargs={decorator_kwargs}")

    def actual_decorator(func):
        """
        Сам декоратор. Принимает декорируемую функцию.
        Возвращает обернутую функцию.
        """
        print(f"actual_decorator: func={func.__name__}")

        def wrapper(*args, **kwargs):
            """
            Обернутая функция. Выполняет логику декоратора до и после вызова декорируемой функции.
            """
            print(f"wrapper: args={args}, kwargs={kwargs}")
            # Логика перед вызовом декорируемой функции
            print("Декоратор: Перед вызовом функции.")

            result = func(*args, **kwargs) # Вызов декорируемой функции

            # Логика после вызова декорируемой функции
            print("Декоратор: После вызова функции.")
            return result

        print("actual_decorator: returning wrapper")
        return wrapper

    print("decorator_factory: returning actual_decorator")
    return actual_decorator

# Пример использования
@decorator_factory("arg1", "arg2", keyword1="value1", keyword2="value2")
def my_function(a, b):
    """Пример декорируемой функции."""
    print(f"my_function: a={a}, b={b}")
    return a + b

result = my_function(10, 20)
print(f"Результат: {result}")
  

Разберем код:

  • decorator_factory: Это фабрика декораторов. Она принимает аргументы (*decorator_args, **decorator_kwargs), которые используются для настройки декоратора. Она возвращает функцию actual_decorator. При вызове @decorator_factory(...) происходит выполнение этого кода.
  • actual_decorator: Это сам декоратор. Он принимает декорируемую функцию (func) в качестве аргумента. Он возвращает функцию wrapper. Этот код выполняется во время применения декоратора (@).
  • wrapper: Это обернутая функция. Она принимает те же аргументы, что и декорируемая функция (*args, **kwargs). Она выполняет логику до и после вызова декорируемой функции. Этот код выполняется каждый раз, когда вызывается декорированная функция.

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

  1. Когда вы используете @decorator_factory("arg1", keyword1="value1") над функцией my_function, вызывается decorator_factory с аргументами "arg1" и keyword1="value1".
  2. decorator_factory возвращает функцию actual_decorator.
  3. Python применяет actual_decorator к my_function, передавая ее в качестве аргумента. То есть, actual_decorator(my_function).
  4. actual_decorator создает функцию wrapper, которая оборачивает my_function.
  5. actual_decorator возвращает wrapper.
  6. Теперь, когда вы вызываете my_function(10, 20), на самом деле вызывается wrapper(10, 20).
  7. wrapper выполняет свою логику (печатает "Декоратор: Перед вызовом функции."), затем вызывает my_function(10, 20).
  8. my_function выполняет свою логику (печатает "my_function: a=10, b=20" и возвращает 30).
  9. wrapper выполняет свою логику после вызова функции (печатает "Декоратор: После вызова функции.") и возвращает результат, полученный от my_function (30).

Преимущества:

  • Настраиваемость: Фабрика декораторов позволяет вам создавать декораторы, которые могут быть настроены с помощью аргументов.
  • Переиспользование: Вы можете использовать один и тот же декоратор с разными настройками для разных функций.
  • Читаемость: Разделение логики декоратора на отдельные функции делает код более читаемым и понятным.

Этот шаблон позволяет создавать гибкие и мощные декораторы в Python, которые можно адаптировать к различным потребностям.

0