isf*_*f85 3 python function python-3.x python-decorators
我很难理解如何将参数传递给装饰器内的包装器函数.举一个简单的例子:
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)
Run Code Online (Sandbox Code Playgroud)
我有一个函数,需要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>
Run Code Online (Sandbox Code Playgroud)
你的理解是完全正确的.装饰器语法只是语法糖,行:
@my_decorator
def my_function(arg):
print(arg + 1)
Run Code Online (Sandbox Code Playgroud)
被执行为
def my_function(arg):
print(arg + 1)
my_function = my_decorator(my_function)
Run Code Online (Sandbox Code Playgroud)
没有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
Run Code Online (Sandbox Code Playgroud)
可以通过__closure__属性访问闭包,您可以通过属性访问闭包的当前值cell_contents.在这里,这是原始装饰的功能对象.
请务必注意,每次调用时my_decorator(),都会创建一个新的函数对象.它们都被命名,wrapper()但它们是独立的对象,每个对象都有自己的对象__closure__.
* Python生成字节码,用于创建函数对象而不将其赋值给名称; 它生活在堆栈上.然后,下一个字节码指令调用装饰器对象:
>>> 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
Run Code Online (Sandbox Code Playgroud)
所以首先LOAD_NAME查找my_decorator名称.接下来,加载为函数对象生成的字节码以及函数的名称.MAKE_FUNCTION从这两条信息中创建函数对象(从堆栈中删除它们)并将生成的函数对象重新放入.CALL_FUNCTION然后在堆栈上获取一个参数(它的操作数1告诉它要采用多少个位置参数),并调用堆栈上的下一个对象(加载装饰器对象).然后将该调用的结果存储在名称下my_function.