嵌套函数装饰器,用于操作python中的参数

And*_*ows 11 python decorator inspect kwargs

我正在编写一个函数装饰器,它将转换应用于函数的第一个参数.如果我只修改一次我的函数,它工作正常,但如果我装饰它们两次我得到一个错误.下面是一些演示该问题的代码,它是我正在处理的代码的简化版本.我已经排除了进行转换的代码,以免分散注意力

from inspect import getargspec
from functools import wraps

def dec(id):
    def _dec(fn):
        @wraps(fn)
        def __dec(*args, **kwargs):
            if len(args):
                return fn(args[0], *args[1:], **kwargs)
            else:
                first_arg = getargspec(fn).args[0]
                new_kwargs = kwargs.copy()
                del new_kwargs[first_arg]
                return fn(kwargs[first_arg], **new_kwargs)
        return __dec
    return _dec

@dec(1)
def functionWithOneDecorator(a, b, c):
    print "functionWithOneDecorator(a = %s, b = %s, c = %s)" % (a, b, c)

@dec(1)
@dec(2)
def functionWithTwoDecorators(a, b, c):
    print "functionWithTwoDecorators(a = %s, b = %s, c = %s)" % (a, b, c)

functionWithOneDecorator(1, 2, 3)
functionWithOneDecorator(1, b=2, c=3)
functionWithOneDecorator(a=1, b=2, c=3)
functionWithOneDecorator(c=3, b=2, a=1)

functionWithTwoDecorators(1, 2, 3)
functionWithTwoDecorators(1, b=2, c=3)
functionWithTwoDecorators(a=1, b=2, c=3)
functionWithTwoDecorators(c=3, b=2, a=1)
Run Code Online (Sandbox Code Playgroud)

当我运行上面的代码时,我得到以下输出:

functionWithOneDecorator(a = 1, b = 2, c = 3)
functionWithOneDecorator(a = 1, b = 2, c = 3)
functionWithOneDecorator(a = 1, b = 2, c = 3)
functionWithOneDecorator(a = 1, b = 2, c = 3)
functionWithTwoDecorators(a = 1, b = 2, c = 3)
functionWithTwoDecorators(a = 1, b = 2, c = 3)
IndexError: list index out of range
Run Code Online (Sandbox Code Playgroud)

这是因为当第二个装饰器检查函数时它正在装饰以找到参数名称并失败,因为它正在装饰一个装饰器并且只需要*args和**kwargs.

我可以想办法在上面的代码中解决这个问题,但是如果一个函数是用我的装饰器装饰而另一个是第三方装饰的话,它仍会破坏.有一般的方法来解决这个问题吗?还是有更好的方法来达到同样的效果?

更新:感谢@Hernan指出装饰模块.它完全解决了这个问题.现在我的代码看起来像这样:

from decorator import decorator

def dec(id):
    @decorator
    def _dec(fn, *args, **kwargs):
        return fn(args[0], *args[1:], **kwargs)
    return _dec

@dec(1)
def functionWithOneDecorator(a, b, c):
    print "functionWithOneDecorator(a = %s, b = %s, c = %s)" % (a, b, c)

@dec(1)
@dec(2)
def functionWithTwoDecorators(a, b, c):
    print "functionWithTwoDecorators(a = %s, b = %s, c = %s)" % (a, b, c)

functionWithOneDecorator(1, 2, 3)
functionWithOneDecorator(1, b=2, c=3)
functionWithOneDecorator(a=1, b=2, c=3)
functionWithOneDecorator(c=3, b=2, a=1)

functionWithTwoDecorators(1, 2, 3)
functionWithTwoDecorators(1, b=2, c=3)
functionWithTwoDecorators(a=1, b=2, c=3)
functionWithTwoDecorators(c=3, b=2, a=1)    
Run Code Online (Sandbox Code Playgroud)

更清洁,它的工作原理!

Her*_*nan 5

问题是,装饰函数的签名不是原始签名(getargspec).在装饰模块的帮助下,您可以很好地解释它,您可以解决您的问题.基本上,您应该使用保留签名的装饰器,以便第二个装饰器看到与第一个相同的签名.