2009-05-20 11 views
6

私はPython関数Iコードのパフォーマンスを計測することができることが好きなので、非常に多くの場合、私はこれに似た何かを...関数デコレータ

import time 

def some_function(arg1, arg2, ..., argN, verbose = True) : 
    t = time.clock() # works best in Windows 
    # t = time.time() # apparently works better in Linux 

    # Function code goes here 

    t = time.clock() - t 
    if verbose : 
     print "some_function executed in",t,"sec." 

    return return_val 

はい、私はあなたがはtimeitとパフォーマンスを測定することになっている知っていますしかし、これは私のニーズにうまく機能し、デバッグのためのこの情報のオンとオフを非常にスムーズに行うことができます。

もちろんこのコードは、私が今、それらについて多くを知っているが、私は、私は、以下の** kwds辞書を用いたデコレータを書くことができると思うこと...私は関数デコレータを知っていた前からではなかったです

some_function(arg1, arg2, ..., argN) # Does not time function 
some_function(arg1, arg2, ..., argN, verbose = True) # Times function 

作業がよりようなものになるように、私はそれにもかかわらず、私の機能の前の作業を複製したいと思います:

some_function(arg1, arg2, ..., argN) # Does not time function 
some_function(arg1, arg2, ..., argN, False) # Does not time function 
some_function(arg1, arg2, ..., argN, True) # Times function 

私は、これは知っている、引数の数をカウントするデコレータが必要になると思います元の関数が何回取るか、ストライトするかpを超過して関数の正しい数を渡してください...これを行うためにPythonに指示する方法は不明ですが...可能ですか?同じことを達成するより良い方法はありますか?

答えて

9

inspectはあなたの道上のビットを得るかもしれないが、何がしたいことはない可能一般的である:

def f(*args): 
    pass 

fはどのように多くの引数を取るのでしょうか? *args**kwargsは任意の数の引数を許すので、関数が必要とする引数の数を決定する方法はありません。実際には、関数が実際にそれをスローされた数だけ処理するケースがあります。


編集:あなたは特別なキーワード引数としてverboseを我慢して喜んでいる場合は、あなたがこれを行うことができ

import time 

def timed(f): 
    def dec(*args, **kwargs): 
     verbose = kwargs.pop('verbose', False) 
     t = time.clock() 

     ret = f(*args, **kwargs) 

     if verbose: 
      print("%s executed in %ds" % (f.__name__, time.clock() - t)) 

     return ret 

    return dec 

@timed 
def add(a, b): 
    return a + b 

print(add(2, 2, verbose=True)) 

(!kwargs.pop先端をありがとうAlex Martelli

+0

が、どのような "some_function(ARG1、ARG2、...、ARGN、トゥルー)" も実行しなければならないのですか? –

+0

さて、別の種類のデコレータが必要です。 inspectを使うか、修飾された関数が取る引数の固定数を示す追加の数値をとります。 – Stephan202

+0

Stephanのためにそれを書いてくれてありがとう!私は仕事から家に帰る途中でこれについて考えていましたが、キーワード引数を使用する方が良い方法ですが、関数の時間を何倍にするかなどの他のパラメータを含めることができるという結論に達しましたプリントアウトに平均時間、最小時間、またはより詳細な統計情報が反映されるべきかどうか。 – Jaime

8

+1 Stephan202の答えでは、(コメントはコードの書式がうまくないので、これを別の答えに入れてください)、その答えの次のビット:

verbose = False 
if 'verbose' in kwargs: 
    verbose = True 
    del kwargs['verbose'] 
は、はるかに明確かつ簡潔に表現することができるよう:

verbose = kwargs.pop('verbose', False) 
+0

+1。前にdictでpop()を使ったことはありませんでした。 – Stephan202

0

それは難しいかもしれないが、あなたはこれらの行に何かを行うことができます。以下のコードは余分な引数を削除しようとし、それらを出力します。

def mydeco(func): 
    def wrap(*args, **kwargs): 
     """ 
     we want to eat any extra argument, so just count args and kwargs 
     and if more(>func.func_code.co_argcount) first take it out from kwargs 
     based on func.func_code.co_varnames, else last one from args 
     """ 
     extraArgs = [] 

     newKwargs = {} 
     for name, value in kwargs.iteritems(): 
      if name in func.func_code.co_varnames: 
       newKwargs[name] = value 
      else: 
       extraArgs.append(kwargs[name]) 

     diff = len(args) + len(newKwargs) - func.func_code.co_argcount 
     if diff: 
      extraArgs.extend(args[-diff:]) 
      args = args[:-diff] 

     func(*args, **newKwargs) 
     print "%s has extra args=%s"%(func.func_name, extraArgs) 

    return wrap 

@mydeco 
def func1(a, b, c=3): 
    pass 

func1(1,b=2,c=3, d="x") 
func1(1,2,3,"y") 

出力は

func1 has extra args=['x'] 
func1 has extra args=['y'] 
関連する問題