Динамические декораторы Python — зачем так много переносов?

#python #decorator

#python #декоратор

Вопрос:

Итак, я все еще новичок в декораторах Python — я использовал их раньше, но никогда не создавал свой собственный. Я читаю этот учебник (этот конкретный абзац) и, кажется, не понимаю, зачем нам нужны три уровня функций? Почему мы не можем сделать что-то подобное:

 def decorator(func, *args, **kwargs):
    return func(*args,**kwargs)
  

Спасибо 🙂

Ответ №1:

Ну, что произойдет, если вы вызовете этот декоратор для функции?

 @decorator
def foo(): pass
  

Этот код немедленно вызвал бы foo , чего мы не хотим. Вызываются декораторы, и их возвращаемое значение заменяет функцию. Это то же самое, что сказать

 def foo(): pass
foo = decorator(foo)
  

Итак, если у нас есть декоратор, вызывающий foo , мы, вероятно, захотим иметь функцию, которая возвращает функцию, вызывающую foo — та функция, которую она возвращает, заменит foo .

 def decorator(f):
    def g(*args, **kwargs):
        return f(*args, **kwargs)
    return g
  

Теперь, если мы хотим передать параметры декоратору, мы не можем точно передать их в ide параллельно с функцией, как в вашем примере. Для этого нет синтаксиса. Итак, мы определяем функцию, которая возвращает параметризованный декоратор. Возвращаемый им декоратор будет замыканием.

 def argument_decorator(will_I_call_f):
    def decorator(f):
        def g(*args, **kwargs):
            if will_I_call_f: return f(*args, **kwargs)
        return g
    return decorator
  

итак, мы можем сделать

 decorator = argument_decorator(True)
@decorator
def foo(): pass
  

И Python предлагает удобный синтаксис, в котором вы вставляете вызов функции:

 @argument_decorator(True)
def foo(): pass
  

И все это синтаксический сахар для синтаксиса без декоратора

 def foo(): pass
foo = argument_decorator(True)(foo)
  

Комментарии:

1. 1 Хорошее объяснение. Пример для последнего «в действии» также был бы полезен, имхо.

2. Шаблон инициализации / вызова — это еще одна альтернатива, когда вы хотите предоставить аргументы вашему декоратору. artima.com/weblogs/viewpost.jsp?thread=240845

3. Вот почему создание класса decorator, хотя и более подробного, иногда может быть более понятным.

4. Лично я бы не стал использовать класс decorator, если бы не хотел поддерживать состояние декоратора. Замыкания читаемы, когда вы к ним привыкли. К сожалению, задавший вопрос не был. 🙁

5. @detly: Классы декораторов вызывают проблемы при применении к методам — поскольку они не являются функциями, объект, обертывающий их, не распознается как метод. Вы должны повторить логику дескрипторов instancemethod (по стандартам Python, это относительно много шаблонного кода, и при этом неочевидного), или self он не будет передан.

Ответ №2:

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