请考虑以下示例。
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)
答案 0 :(得分:2)
想象一下,是否可以将装饰器应用于常规变量分配,如下所示:
def add1(x):
return x + 1
@add1
number = 5
与函数装饰器类似的行为如下:
number = 5
number = add1(number)
这将导致将值6
分配给变量number
。现在想象一下装饰器刚刚被调用而没有返回任何内容:
number = 5
add1(number)
由于6
是passed 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
指令将这两个对象变成了留在堆栈上的函数。CALL_FUNCTION
指令使用一个参数decorate
函数调用func
函数(仍在堆栈中)。decorate
函数返回的任何内容都保留在堆栈上,并由func
指令分配给名称STORE_NAME
。因此,如果decorator
函数未返回任何内容,则将没有任何内容可分配给名称func
-甚至没有def
语句中的原始函数。