我很难理解如何将参数传递给装饰器内的包装器函数。 举一个简单的例子:
def my_decorator(func):
def wrapper(func_arg):
print('Before')
func(func_arg)
print('After')
return wrapper
@my_decorator
def my_function(arg):
print(arg + 1)
my_function(1)
我有一个带有1个参数的函数,它被装饰了。我在理解func_arg如何工作方面遇到了麻烦。调用my_function(1)时,值1如何传递给包装器。从我对此的一点了解,是my_function被'替换'了 一个新功能,如:my_function = my_decorator(my_function)。
print(my_function)
<function my_decorator.<locals>.wrapper at 0x7f72fea9c620>
答案 0 :(得分:6)
您的理解完全正确。装饰器语法只是语法糖,行:
@my_decorator
def my_function(arg):
print(arg + 1)
以
执行def my_function(arg):
print(arg + 1)
my_function = my_decorator(my_function)
在装饰者被称为 * 之前,实际上没有设置my_function
。
因此,my_function
现已绑定到wrapper()
函数中创建的my_decorator()
函数。 原始函数对象作为my_decorator()
参数传递到func
,因此仍可用于wrapper()
函数,作为闭包。因此,调用func()
会调用原始函数对象。
因此,当您调用已装饰的my_function(1)
对象时,您真的会调用wrapper(1)
。此函数通过名称1
接收func_arg
,然后wrapper()
本身调用func(func_arg)
,这是原始函数对象。所以最后,原始函数也通过了1
。
您可以在解释器中看到此结果:
>>> def my_decorator(func):
... def wrapper(func_arg):
... print('Before')
... func(func_arg)
... print('After')
... return wrapper
...
>>> @my_decorator
... def my_function(arg):
... print(arg + 1)
...
>>> my_function
<function my_decorator.<locals>.wrapper at 0x10f278ea0>
>>> my_function.__closure__
(<cell at 0x10ecdf498: function object at 0x10ece9730>,)
>>> my_function.__closure__[0].cell_contents
<function my_function at 0x10ece9730>
>>> my_function.__closure__[0].cell_contents(1)
2
可以通过__closure__
属性访问闭包,您可以通过cell_contents
属性访问闭包的当前值。这是原始修饰的函数对象。
请务必注意,每次调用my_decorator()
时,都会创建一个 new 函数对象。它们都是wrapper()
,但它们是单独的对象,每个对象都有自己的__closure__
。
* Python生成字节码,用于创建函数 object 而不将其分配给名称;它生活在堆栈上。然后,下一个字节码指令调用装饰器对象:
>>> import dis
>>> dis.dis(compile('@my_decorator\ndef my_function(arg):\n print(arg + 1)\n', '', 'exec'))
1 0 LOAD_NAME 0 (my_decorator)
2 LOAD_CONST 0 (<code object my_function at 0x10f25bb70, file "", line 1>)
4 LOAD_CONST 1 ('my_function')
6 MAKE_FUNCTION 0
8 CALL_FUNCTION 1
10 STORE_NAME 1 (my_function)
12 LOAD_CONST 2 (None)
14 RETURN_VALUE
首先LOAD_NAME
查找my_decorator
名称。接下来,加载为函数对象生成的字节码以及函数的名称。 MAKE_FUNCTION
从这两条信息中创建函数对象(从堆栈中删除它们)并将生成的函数对象重新放入。 CALL_FUNCTION
然后在堆栈上获取一个参数(它的操作数1
告诉它要采用多少个位置参数),并调用堆栈上的下一个对象(加载装饰器对象)。然后,该调用的结果将存储在名称my_function
。
答案 1 :(得分:2)
Python decorator
是一个函数,它将另一个函数作为参数,生成一个新函数。
def my_decorator(func):
def wrapper(func_arg):
print('Before')
func(func_arg)
print('After')
return wrapper
@my_decorator
def my_function(arg):
print(arg + 1)
所以这实际上与:
相同my_function = my_decorator(my_function)
这意味着此装饰器的返回值将替换原始函数定义。
只需在另一个答案中添加几点。您可以通过任意数量的其他功能(装饰器的堆叠)来装饰您的my_function
。当我们堆叠多个装饰器时,执行顺序是从最里面到最外层。
例如:
@i_get_called_last
@i_get_called_second
@i_get_called_first
def my_function(arg):
print(arg + 1)
在这里,您需要确保第一个和第二个被调用的装饰器返回兼容的函数,前面的装饰器可以使用它们。
您可以通过this了解更多内容。