functools.wraps 的作用

本文翻译自Stackoverflow:What does functools.wraps do?

当你使用一个装饰器,就是用另一个函数替换当前函数。换一种说法,如果你有一个装饰器:

1
2
3
4
5
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging

你这样使用它:

1
2
3
4
@logged
def f(x):
"""does some math"""
return x + x * x

实际上和这种用法相同:

1
2
3
4
def f(x):
"""does some math"""
return x + x * x
f = logged(f)

而且你的函数 fwith_loging 替换。不幸的是,这意味着当你使用:

1
print f.__name___

它会打印出 with_logging 因为这是你新函数的名字。事实上,如果你查看 f 的 文档字符串,它将是空的,因为 with_logging 没有文档字符串所以你写的文档字符串不会在这里出现。并且,如果你查看这个函数使用 pydoc 生成结果,他的参数列表不是一个参数 x,取而代之的是 *args**kwargs因为这是 with_logging 所持有的。

如果使用装饰器总是意味着丢失这个函数的信息,这将是个严重的问题。这就是为什么我们有 functools.wraps 的原因。给函数使用一个装饰器并且给函数增加复制名字、文档字符串、参数列表等功能性(This takes a function used in a decorator and adds the functionality of copying over the function name, docstring, arguments list, etc)。当 wraps 是它自己的装饰器,下边的代码将会做正确的事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print f.__name__ # prints 'f'
print f.__doc__ # prints 'does some math'