@
在python中是函数的修饰符,为函数提供了包装的功能,为函数提供了更细粒度的扩展控制。详见官方文档。
本文仅从使用的角度来探究一下@
的特性。
例1
1 | def wrapper(fn): |
输出为:
1 | This is a wrapper! |
在执行line 9
前这里并没有任何函数调用,但是wrapper
已经被执行了。这是因为在解析到@wrapper
的时候,等效的代码为:
1 | func = wrapper(func) |
这也就解释了为什么调用func
会报NoneType
的错误(wrapper
没有return value
,因此func
为None
)。wrapper
的参数fn
实际上就是func
,在wrapper末尾加上return fn
,程序的输出为:
1 | This is a wrapper! |
例2
1 | def wrapper(base): |
输出为:
1 | base = 8 |
wrapper
也可以有参数,这里接收一个参数,func
就不能像例1那样直接传过来,而是要在wrapper里面新定义一个函数(名称随意),这个内部函数将会得到被包装的函数。
进一步地想,在wrapper里面既然获得了原函数,我们能否获得原函数的参数?
例3
1 | def wrapper(base): |
输出为:
1 | base = 8 |
在inner
里面再定义一个内部函数,这个函数就可以获得原函数的参数。在这个例子中,我们将这个内部函数返回,而且这个内部函数还调用了原函数。
在真实的场景中,往往还会用到fn.__name__
来获取被包装的函数的名称来定制化包装。
这里就到尽头了,在new_func
中再定义函数就不会得到任何被包装函数的信息,就和普通的嵌套函数类似了。
例4
1 | def wrapper(base): |
输出为
1 | base = 8 |
函数包装是可以嵌套的,这里将wrapper
包装后的函数传入wrapper2
,wrapper2
如例1所示,接收一个参数,这个参数即是wrapper
包装后返回的函数new_func
。wrapper2
中调用fn
,得到了预料中的输出result = 40
。