以最常见的time_it装饰器函数为例, 如下代码中:
-
第一个time_it函数没有使用functools.wraps, 功能上这个装饰器并没有什么问题;
-
第二个better_time_it在wrapper函数面前又加了一个@functools.wraps(func)函数, 其他代码一致;
import time
import functools
def time_it(func):
def wrapper(*args, **kwargs):
"""This is docs for wrapper"""
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print('Running "%s" in %.3f seconds' % (func.__name__, (end - start)))
return result
return wrapper
def better_time_it(func):
@functools.wraps(func) # Pay attention here!
def wrapper(*args, **kwargs):
"""This is docs for wrapper"""
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print('Running "%s" in %.3f seconds' % (func.__name__, (end - start)))
return result
return wrapper
我们的测试代码如下:
@time_it
def foo():
"""This is docs foo function"""
print('Foo!')
@better_time_it
def yeah():
"""This is docs yeah function"""
print('Yeah!')
if __name__ == '__main__':
help(foo)
help(yeah)
这里我用help函数查看function的doc strings,得到如下结果:
Help on function wrapper in module __main__:
wrapper(*args, **kwargs)
This is docs for wrapper
Help on function yeah in module __main__:
yeah()
This is docs yeah function
-
可以看到使用time_it的foo函数的docs变成了wrapper函数的docs;
-
而使用better_time_it的yeah函数的docs才是我们期望得到的实际docs;
不止help函数的结果会有变化,因为使用装饰器实际上返回的并不是原函数本身,所以原函数相关的metadata信息已经变化了,如果不使用functools.wraps函数就会导致此类副作用,比如序列化和debugger时也会使结果混乱,所以养成良好的习惯,自定义decorator的时应该尽量使用functools.wraps
关于functools.wraps()函数的官方文档中写道:
The main intended use for this function is in decorator functions which wrap the decorated function and return the wrapper. If the wrapper function is not updated, the metadata of the returned function will reflect the wrapper definition rather than the original function definition, which is typically less than helpful.
网友评论