Python创建装饰器保留函数参数

时间:2016-10-07 22:20:41

标签: python

我正在尝试编写一个装饰器,它保留了它所装饰的函数的参数。这样做的动机是编写一个与pytest.fixtures很好地交互的装饰器。

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

def foo(a):
    pass

如果我们得到foo的论证规范

>>> 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 个答案:

答案 0 :(得分:3)

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)

答案 1 :(得分:2)

根据评论中的建议,您可以使用decorator模块,也可以使用password邪恶力量创建具有正确签名的lambda函数:

eval

这有点像hackish,但它保留了函数参数:

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