11

関数の最初の引数に変換を適用する関数デコレータを書いています。関数を1回だけ装飾すると問題なく動作しますが、2回装飾するとエラーが発生します。以下は問題を示すいくつかのコードです、それは私が取り組んでいるコードの単純化されたバージョンです。問題を邪魔しないように、変換を行うコードを除外しました

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

これは、2番目のデコレータが関数を検査するときに、引数名を見つけるためにデコレーションしているのに失敗し、デコレータをデコレータしているために失敗し、*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)    

はるかにきれいで、それは機能します!

4

1 に答える 1

5

問題は、装飾された関数の署名が元の関数の署名(getargspec)ではないことです。それはあなたがあなたの問題を解決することができるデコレータモジュールの助けを借りて本当によく説明されています。基本的に、2番目のデコレータが最初のデコレータと同じ署名を認識できるように、署名を保持するデコレータを使用する必要があります。

于 2011-10-09T22:29:58.790 に答える