2011-10-09 19 views
10

私は関数の最初の引数に変換を適用する関数デコレータを作成しています。私の機能を一度しか飾らないとうまくいきますが、二度飾るとエラーになります。以下は問題を示すコードですが、これは私が作業しているコードの簡略化されたバージョンです。ときに、第2のデコレータためですPythonの引数で動作するネストされた関数デコレータ

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 

:問題から

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) 

をそらすないように私は上記のコードを実行すると、私は次のような出力を得るための変換を行うコードを除外しています引数名を見つけるために装飾している関数を検査し、デコレータを装飾していて、* argsと** kwargsしか必要としないので失敗します。

私は上のコードではうまくいくかもしれないが、関数が私のデコレータと第三者からの別のもので装飾されていれば、やはり壊れてしまうと考えられます。これを修正する一般的な方法はありますか?または同じ結果を達成するためのより良い方法がありますか?

更新:decorator moduleを指摘してくれた@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)  

多くのクリーナー、およびそれは働きます!

+1

なぜ 'args [0]、* args [1:]'、 '* args'と同じですか? –

+0

このデコレータで解決しようとしている問題は何ですか?私の言うところの主目的は、最初の指定された引数(キーワード/オプションまたはそれ以外)が、常にラップされた関数に「最初の」引数として渡されることを確認することです。また、デコレータに対する 'id'引数の意図する意義は何ですか?それはどこにも使われていません。 –

+0

最初の引数に変換を適用したいと思います。上記のコードでは、問題を気にさせないように変換を行うコードを除外しました。 –

答えて

5

問題は、装飾された機能の署名がオリジナルの署名(getargspec)ではないことです。それはあなたの問題を解決することができるdecorator moduleの助けを借りて本当によく説明されています。基本的には、署名を保持するデコレータを使用して、2番目のデコレータが最初のデコレータと同じシグネチャを表示するようにしてください。

+0

鮮やかな、まさに私が探していたものです。 –

関連する問題