2011-10-09 39 views
10

我在写一个函数装饰器,它将对函数的第一个参数进行转换。它工作正常,如果我只装饰我的功能一次,但如果我装饰他们两次我得到一个错误。下面是一些演示问题的代码,它是我正在处理的代码的简化版本。我已经排除执行转换,从而当我运行上面的代码中,我得到以下输出不从问题对python中的参数进行操作的嵌套函数装饰器

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) 

分散代码:

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 

这是因为,当第二装饰检查正在装饰的函数以查找参数名称,并且因为装饰装饰器而失败,并且仅使用* args和** kwargs。

我可以想办法解决上述代码中的问题,但如果一个函数用我的装饰器和另一个来自第三方来装饰,它仍会破坏。有没有一个通用的方法来解决这个问题?还是有更好的方法来实现相同的结果?

更新:感谢@Hernan指出decorator module。它完全解决了这个问题。现在我的代码如下所示:

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)  

更清洁,并它的作品!

+1

为什么'args [0],* args [1:]',它与'* args'相同? –

+0

你想用这个装饰器解决什么问题?据我可以告诉它的主要目标似乎是确保第一个给定的参数 - 关键字/可选或其他 - 总是传递给包装函数,因为它是“第一个”参数。另外,装饰器的'id'参数的意图是什么?它不在任何地方使用。 –

+0

我想将转换应用于第一个参数。在上面提供的代码中,我排除了执行转换的代码,以避免分散注意力。 –

回答

5

问题在于你的装饰函数的签名不是原始签名(getargspec)。在decorator module的帮助下,您可以解决您的问题。基本上,您应该使用保留签名的装饰器,以便第二个装饰器可以看到与第一个相同的签名。

+0

辉煌,那正是我所期待的。 –