2012-04-16 24 views
64

私はpythonデコレータに余分な引数を渡すには?

TypeError: myDecorator() takes exactly 2 arguments (1 given) 

はなぜ、私は

def myDecorator(test_func,logIt): 
    if logIt: 
     print "Calling Function: " + test_func.__name__ 
    return callSomeWrapper(test_func) 
@myDecorator(False) 
def someFunc(): 
    print 'Hello' 

以下のような別の引数を受け入れるために、このデコレータを強化したい。しかし、このコードはエラーになります

def myDecorator(test_func): 
    return callSomeWrapper(test_func) 
def callSomeWrapper(test_func): 
    return test_func 
@myDecorator 
def someFunc(): 
    print 'hello' 

以下のようなデコレータを持っています関数は自動的に渡されませんか? 関数をデコレータ関数に明示的に渡すにはどうすればよいですか?

+2

balki:あなたの引数としてブール値を使用して避けてください、それはGDのアプローチではなく、コードのreadliability –

+7

@KitHoを軽減 - それはそうブール値を使用すると、正しいアプローチで、ブールフラグです。 – AKX

+0

@KitHo - "gd"とは何ですか?それは「良い」ですか? –

答えて

107

あなたは機能のようなデコレータを呼び出しているので、それが実際のデコレータで別の関数を返す必要がある:

def my_decorator(param): 
    def actual_decorator(func): 
     print("Decorating function {}, with parameter {}".format(func.__name__, param)) 
     return function_wrapper(func) # assume we defined a wrapper somewhere 
    return actual_decorator 

外側の関数を使用して、明示的に渡された引数が与えられますし、内側を返す必要があります関数。内部関数は、装飾する関数を渡され、変更された関数を返します。

通常、デコレータは関数の動作をラッパー関数でラップすることで変更します。

def log_decorator(log_enabled): 
    def actual_decorator(func): 
     @functools.wraps(func) 
     def wrapper(*args, **kwargs): 
      if log_enabled: 
       print("Calling Function: " + func.__name__) 
      return func(*args, **kwargs) 
     return wrapper 
    return actual_decorator 

ラッパー関数の名前とドキュメンテーション文字列などのfunctools.wrapsコールをコピーする事は、元の関数にそれをより近づけるために:ここでは関数が呼び出されたときに、必要に応じてログを追加する例を示します。

使用例:

>>> @log_decorator(True) 
... def f(x): 
...  return x+1 
... 
>>> f(4) 
Calling Function: f 
5 
+10

そして、 '' functools.wraps'(http://docs.python.org/library/functools.html#functools.wraps)を使用することをお勧めします - ラップされた関数の元の名前、ドキュメントストリングなどを保持します。 – AKX

+0

@AKX:ありがとう、私はこれを2番目の例に追加しました。 – interjay

+1

したがって、基本的にデコレータは関数である引数を1つだけ取ります。しかし、デコレータは、引数を取る可能性のある関数の戻り値にすることができます。これは正しいです? – balki

34

だけ異なる視点を提供する:構文

@expr 
def func(...): #stuff 

は特に

def func(...): #stuff 
func = expr(func) 

に相当し、exprはあなたが好きな何もすることができ、呼び出し可能と評価されている限りです。 特にの場合、exprはデコレータファクトリにすることができます:いくつかのパラメータを与えて、デコレータを与えます。そのため、おそらくあなたの状況を理解するためのより良い方法が

そしてもちろん

@decorator_factory(*args) 
def func(...): 

に短縮することができ

dec = decorator_factory(*args) 
@dec 
def func(...): 

ようですdecorator_factoryのようなデコレータでそれが見えることから、人々はそれに名前を付ける傾向がありますそれを反映させる。インダイレクションのレベルに従おうとすると混乱することがあります。

14

デコレータ引数をオプションにすることができる便利なトリックを追加したいだけです。また、デコレータを再利用してネストを減らすこともできます。

import functools 

def myDecorator(test_func=None,logIt=None): 
    if not test_func: 
     return functools.partial(myDecorator, logIt=logIt) 
    @functools.wraps(test_func) 
    def f(*args, **kwargs): 
     if logIt==1: 
      print 'Logging level 1 for {}'.format(test_func.__name__) 
     if logIt==2: 
      print 'Logging level 2 for {}'.format(test_func.__name__) 
     return test_func(*args, **kwargs) 
    return f 

#new decorator 
myDecorator_2 = myDecorator(logIt=2) 

@myDecorator(logIt=2) 
def pow2(i): 
    return i**2 

@myDecorator 
def pow3(i): 
    return i**3 

@myDecorator_2 
def pow4(i): 
    return i**4 

print pow2(2) 
print pow3(2) 
print pow4(2) 
1

デコレータを行うもう1つの方法です。 私はこの方法が私の頭を包み込むのが最も簡単だと思う。

import functools 

class NiceDecorator: 
    def __init__(self, param_foo='a', param_bar='b'): 
     self.param_foo = param_foo 
     self.param_bar = param_bar 

    def __call__(self, func): 
     @functools.wraps(func) 
     def my_logic(*args, **kwargs): 
      # whatever logic your decorator is supposed to implement goes in here 
      print('pre action baz') 
      print(self.param_bar) 
      # including the call to the decorated function (if you want to do that) 
      result = func(*args, **kwargs) 
      print('post action beep') 
      return result 

     return my_logic 

# usage example from here on 
@NiceDecorator(param_bar='baaar') 
def example(): 
    print('example yay') 


example() 
関連する問題