本随笔是对Python札记 -- 装饰器的一些补充。
使用装饰器的时候,被装饰函数的一些属性会丢失,比如如下代码:
1 #!/usr/bin/env python 2 3 def deco(func): 4 def wrapper(): 5 print "Wrap start" 6 func() 7 print "Wrap end " 8 return wrapper 9 10 @deco 11 def foo(): 12 """Docstring for foo""" 13 print "In foo():" 14 15 foo() 16 print foo.__name__ 17 print foo.__doc__
输出如下:
$ python decorator_test.py
Wrap start
In foo():
Wrap end
wrapper
None
可以发现,__name__属性成了wrapper,而__doc__属性则成了None。这对于平常使用多少带来些不便,总不能给每个使用装饰器的函数都重写__name__和__doc__吧。
Python的functools提供的update_wrapper和wraps可以有效解决这个问题。不过update_wrapper是用方法的形式进行调用,而wraps则是用装饰器来封装了update_wrapper。示例代码分别如下:
1 #!/usr/bin/env python 2 from functools import update_wrapper 3 4 def deco(func): 5 def wrapper(): 6 print "Wrap start" 7 func() 8 print "Wrap end " 9 return update_wrapper(wrapper,func) #调用update_wrapper方法 10 11 @deco 12 def foo(): 13 """Docstring for foo""" 14 print "In foo():" 15 16 foo() 17 print foo.__name__ 18 print foo.__doc__
1 #!/usr/bin/env python 2 from functools import wraps 3 4 def deco(func): 5 @wraps(func) #使用装饰器来实现 6 def wrapper(): 7 print "Wrap start" 8 func() 9 print "Wrap end " 10 return wrapper 11 12 @deco 13 def foo(): 14 """Docstring for foo""" 15 print "In foo():" 16 17 foo() 18 print foo.__name__ 19 print foo.__doc__
现在被deco装饰过的foo方法,可以保留之前的__name__和__doc__属性了。
$ python decorator_test_with_update_wrapper.py Wrap start In foo(): Wrap end foo Docstring for foo $ python decorator_test_with_wraps.py Wrap start In foo(): Wrap end foo Docstring for foo