装饰器函数为什么有返回值?

时间:2020-03-10 19:02:08

标签: python decorator python-decorators

请考虑以下示例。

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的目的是什么?


编辑

装饰器比简单的包装器还能做更多的事情吗?

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 个答案:

答案 0 :(得分:2)

想象一下,是否可以将装饰器应用于常规变量分配,如下所示:

def add1(x):
    return x + 1

@add1
number = 5

与函数装饰器类似的行为如下:

number = 5
number = add1(number)

这将导致将值6分配给变量number。现在想象一下装饰器刚刚被调用而没有返回任何内容:

number = 5
add1(number)

由于6passed by value, not by reference,因此该代码不可能将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指令将这两个对象变成了留在堆栈上的函数。
  • li>
  • CALL_FUNCTION指令使用一个参数decorate函数调用func函数(仍在堆栈中)。
  • decorate函数返回的任何内容都保留在堆栈上,并由func指令分配给名称STORE_NAME

因此,如果decorator函数未返回任何内容,则将没有任何内容可分配给名称func-甚至没有def语句中的原始函数。