functools.wraps做什么?

在对另一个问题的回答发表评论时,有人说他们不确定functools.wraps在做什么。 所以我问这个问题,以便在StackOverflow上有一个logging供将来参考:functools.wraps究竟做了什么?

当你使用装饰器时,你正在用另一个replace一个函数。 换句话说,如果你有一个装饰器

def logged(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 

说完全一样

 def f(x): """does some math""" return x + x * x f = logged(f) 

你的函数f被replace为with_logging函数。 不幸的是,这意味着如果你那么说

 print f.__name__ 

它会打印with_logging因为这是你的新function的名称。 实际上,如果你查看f的文档string,它将是空白的,因为with_logging没有文档string,所以你写的文档string不会再存在。 另外,如果您查看该函数的pydoc结果,则不会将其列为一个参数x ; 相反,它会被列为采取*args**kwargs因为这是什么with_logging需要。

如果使用装饰器总是意味着丢失关于某个函数的信息,那将是一个严重的问题。 这就是为什么我们有functools.wraps 。 这需要一个在装饰器中使用的函数,并在函数名称,docstring,参数列表等等上添加了复制的function。由于wraps本身就是一个装饰器,所以下面的代码会做正确的事情:

 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' 

我经常使用类,而不是函数,为我的装饰。 我遇到了一些麻烦,因为一个对象不会有一个函数所期望的所有相同的属性。 例如,一个对象不会有__name__属性。 我有一个具体的问题,这是很难跟踪哪里Django报告的错误“对象没有属性__name__ ”“。 不幸的是,对于阶级风格的装饰者,我不相信@wrap会做这个工作。 我已经创build了一个基本的装饰类,如下所示:

 class DecBase(object): func = None def __init__(self, func): self.__func = func def __getattribute__(self, name): if name == "func": return super(DecBase, self).__getattribute__(name) return self.func.__getattribute__(name) def __setattr__(self, name, value): if name == "func": return super(DecBase, self).__setattr__(name, value) return self.func.__setattr__(name, value) 

该类将所有属性调用代理到正在装饰的函数。 所以,你现在可以创build一个简单的装饰器,检查2个参数是如何指定的:

 class process_login(DecBase): def __call__(self, *args): if len(args) != 2: raise Exception("You can only specify two arguments") return self.func(*args)