考虑以下示例。
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)
想象一下,如果您可以将装饰器应用于常规变量赋值,比如:
def add1(x):
return x + 1
@add1
number = 5
类似于函数装饰器的行为如下:
number = 5
number = add1(number)
这将导致将值6
分配给变量number
。现在想象一下,decorator只是被调用而没有返回任何内容:
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)
因此,函数装饰器的行为以与上述相同的方式工作,原因相同;decorator函数不能在完全不同的作用域中将新函数重新分配给变量func
,因为func
是通过值而不是通过引用传递给decorator的。
func = decorator(func)
等效实际上有点误导。为了完全正确,当您使用decorator时,您在def
语句中定义的函数会直接传递给decorator,而不是在传递之前分配给本地名称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)
循序渐进:
- CCD_ 19函数被加载到堆栈上
func
的代码对象被加载到堆栈上,然后是字符串'func'
,然后是MAKE_FUNCTION
指令将这两者转换为一个留在堆栈上的函数CALL_FUNCTION
指令用一个参数func
函数调用decorate
函数(该函数仍在堆栈中(- 无论
decorate
函数返回什么,都留在堆栈上,并由STORE_NAME
指令分配给名称func
因此,如果decorator
函数没有返回任何内容,那么就不会为名称func
分配任何内容,甚至不会为def
语句中的原始函数分配任何内容。