2016-10-07 28 views
6

我想写一个装饰器,它保留了它装饰的函数的参数。这样做的动机是编写一个与pytest.fixtures很好地交互的装饰器。Python创建装饰保存函数参数

假设我们有一个函数foo。它只需要一个参数a

def foo(a): 
    pass 

如果我们得到富

>>> inspect.getargspec(foo) 
ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None) 

我们经常要创建其中wrapper函数将它的所有参数逐字到wrapped功能的装饰的参数规格。最明显的方法是使用*args**kwargs

def identity_decorator(wrapped): 
    def wrapper(*args, **kwargs): 
     return wrapped(*args, **kwargs) 
    return wrapper 

    def identity_decorator(wrapped): 
    def wrapper(*args, **kwargs): 
     return wrapped(*args, **kwargs) 
    return wrapper 

@identity_decorator 
def foo(a): 
    pass 

此,这并不奇怪,产生一个函数与一个参数规格反映*args**kwargs

>>> inspect.getargspec(foo) 
ArgSpec(args=[], varargs='args', keywords='kwargs', defaults=None) 

有没有办法改变参数规范来匹配被包装的函数或者最初创建带有正确参数规范的函数?

+2

我想在最近的Python版本中,'functools.wraps'做一些事情,使'inspect.getargspec'和'检查.signature'报告包装函数的签名,但它不会更改包装器的真实签名。 ['decorator'](https://pypi.python.org/pypi/decorator)第三方库通过我从来没有深入研究过的方法提供了真正的保留签名的装饰器功能,可能涉及像字节码重写之类的东西。我从来没有用过它。 – user2357112

+0

也许检查一些像http://stackoverflow.com/questions/3729378/how-can-i-programmatically-change-the-argspec-of-a-function-in-a-python-decorato – Nf4r

回答

2

正如评论所说,你可以使用decorator模块,也可以使用eval邪恶力量创造有正确的签名lambda函数:

import inspect 

def identity_decorator(wrapped): 
    argspec = inspect.getargspec(wrapped) 
    args = inspect.formatargspec(*argspec) 

    def wrapper(*args, **kwargs): 

     return wrapped(*args, **kwargs) 

    func = eval('lambda %s: wrapper%s' % (args.strip('()'), args), locals()) 

    return func 

@identity_decorator 
def foo(a): 
    pass 

这是有点hackish的,但它保留了功能参数:

>>> inspect.getargspec(foo) 
ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None) 
1

AFAIK,也可以仅因为Python 3.3与Signature对象:

def identity_decorator(wrapped): 
    def wrapper(*args, **kwargs): 
     return wrapped(*args, **kwargs) 
    wrapper.__signature__ = inspect.signature(wrapped) # the magic is here! 
    return wrapper 

然后,你可以这样做:

@identity_decorator 
def foo(a): 
    pass 

最后:

>>> inspect.getargspec(foo) 
ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None)