Почему функция декоратора имеет возвращаемое значение? - PullRequest
0 голосов
/ 10 марта 2020

Рассмотрим следующий пример.

def decorator(function_to_decorate):
    def wrapper():
        print('Entering', function_to_decorate.__name__)
        function_to_decorate()
        print('Exiting', function_to_decorate.__name__)
    return wrapper

@decorator
def func():
    print("Original function.")

func()

Поскольку синтаксис @decorator является просто сокращением для func = my_decorator(func), логично, что my_decorator должен что-то возвращать. Мой вопрос: почему декораторы определены таким образом, а не без возвращаемого значения: my_decorator(func)? Какова цель возврата функции-оболочки wrapper?


EDIT

Как декоратор делает больше, чем простая оболочка?

def wrapper(function_to_decorate):
    print('Entering', function_to_decorate.__name__)
    function_to_decorate()
    print('Exiting', function_to_decorate.__name__)
def func():
    print("Original function.")
wrapper(func)

1 Ответ

2 голосов
/ 10 марта 2020

Представьте себе, если бы вы могли применить декоратор к присваиванию обычной переменной, например:

def add1(x):
    return x + 1

@add1
number = 5

Аналогичное поведение для декоратора функции было бы следующим:

number = 5
number = add1(number)

This приведет к присвоению значения 6 переменной number. Теперь представьте, что декоратор был просто вызван, ничего не возвращая:

number = 5
add1(number)

Нет способа, которым этот код мог бы присвоить 6 переменной number, потому что number это , переданный значение не по ссылке ; в Python функция не может присвоить новое значение переменной в совершенно другой области, к которой она не имеет доступа.


Оператор def действительно является своего рода присваиванием; он присваивает функции имя, с которым вы ее определили. Например, определение функции def func(): pass компилируется в байт-код, который выполняет STORE_NAME, то есть присваивание:

  1     0 LOAD_CONST         0 (<code object func at ...>)
        3 LOAD_CONST         1 ('func')
        6 MAKE_FUNCTION      0
        9 STORE_NAME         0 (func)

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

Эквивалентность func = decorator(func) фактически является немного вводит в заблуждение. Чтобы быть полностью правильным, когда вы используете декоратор, функция, которую вы определили в операторе def, передается непосредственно декоратору, а не присваивается локальному имени func перед передачей. Вот байт-код:

  1     0 LOAD_NAME          0 (decorate)
        3 LOAD_CONST         0 (<code object func at ...>)
        6 LOAD_CONST         1 ('func')
        9 MAKE_FUNCTION      0
       12 CALL_FUNCTION      1 (1 positional, 0 keyword pair)
       15 STORE_NAME         1 (func)

Пошагово:

  • Функция decorate загружена в стек,
  • Объект кода для func загружается в стек, затем строка 'func', затем инструкция MAKE_FUNCTION превращает эти два в функцию, оставленную в стеке.
  • Инструкция CALL_FUNCTION вызывает decorate функция (которая все еще находится в стеке) с одним аргументом, функция func.
  • Независимо от того, что возвращает функция decorate, она остается в стеке и присваивается имени func с помощью STORE_NAME инструкция.

Так что, если функция decorator ничего не возвращает, присваивать имя func будет нечего - даже оригинальная функция, как в операторе def .

...