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)