Python includes a more natural way of decorating a function by using an annotation on the function that is decorated.
https://en.wikipedia.org/wiki/Decorator_pattern
译:Python提供了一种更为自然的方式来装饰一个函数,这种方法通过在被装饰的函数前加一个注释来时实现。
装饰器原理
看一个简单的例子:1
2
3
4
5
6
7
8
9
10
11
12def test(func):
def wrapper():
func()
print func.__name__, "test wrapper"
return wrapper
def main():
print "start main"
main()
print main.__name__
得到的结果是1
2
3start main
main test wrapper
wrapper
可以看到装饰器会改变函数的属性1
2print main
<function wrapper at 0x7f79c2437938>
此时执行main函数已经被装饰器test返回的wrapper函数给替代了,即 main() == test()()
再看1
2
3
4
5
6
7
8
9def test(test_arg):
print test_arg
def wrapper(func):
print "inner_wrapper"
def inner_wrapper():
func()
print func.__name__, "test wrapper"
return inner_wrapper
return wrapper
- 这时使用装饰器test必须给参数, 如果不给的话test_arg相当与main函数的地址,
- 此时调用wrapper会报错(原因是参数不够)。
- 如果去了wrapper函数的参数则inner_wrapper不会被调用
- 可以看出带参数的装饰器其实包装了一层外壳,先执行最外层test函数的内容,
- 然后将调用的函数的地址传入里层的装饰器
还有一个问题就是之前代码中的func.name和main.name的问题,
不难看出其实main函数的地址就是wrapper的地址,所以main.name打印的是wrapper函数的名字,
那么如何获得main函数真正的属性呢?
functools.wraps
1 | def test(func): |
加上functools.wraps装饰器后的打印结果如下:1
2
3start main
main test wrapper
main
进入functools中看warps函数的实现其实可以看到
functools.wraps函数将wrapper函数的属性使用setattr设置为传入的func即main函数的属性