2011-07-06 13 views
4

私たちは、別の社内チームが提供するライブラリを使用します。Python:ライブラリ内のすべての関数をラップする

from externalTeam import dataCreator 
datacreator.createPizza() 
datacreator.createBurger() 
datacreator.createHotDog() 

最近、特定の状況で実行するために、1つの方法が1分以上かかることがわかりました。これをデバッグするには、このメソッドの呼び出しのたびにコードに入り、タイムアウトを追加する必要がありました。後知恵で

import time 
from externalTeam import dataCreator 
start = time.clock() 
datacreator.createPizza() 
stop = time.clock() 
print "It took %s seconds to perform createPizza" % (str(stop-start)) 

我々はすべての場所でcreatePizzaを呼んでいる、と私たちはcreatePizza自体(アナロジーはここで少しを打破するために開始された)をコントロールしていないので、それはです。むしろ1つの場所でcreatePizzaを呼び出し、その周りにタイマーを追加できるようにしたいと思います。これを達成するための私の最初の考えは、自分のラッパークラスですべてのメソッドをラップすることです。それはしかしDRYの反対だ、といつでも彼らは私もあることをラップするために私たちのライブラリを更新する必要があると思います別のメソッドを追加します。私が欲しいもの

import time 
from externalTeam import dataCreator 
def createPizza(self): 
    start = time.clock() 
    datacreator.createPizza() 
    stop = time.clock() 
    print "It took %s seconds to perform createPizza" % (str(stop-start)) 

def createBurger(self): 
    start = time.clock() 
    datacreator.createPizza() 
    stop = time.clock() 
    print "It took %s seconds to perform createBurger" % (str(stop-start)) 

def createHotDog(self): 
    start = time.clock() 
    datacreator.createPizza() 
    stop = time.clock() 
    print "It took %s seconds to perform createHotDog" % (str(stop-start))  

は常に周りのすべての数行のコードを実行するための方法ですdataCreatorから呼び出されている関数です。そのメソッドを動的に定義することができる、またはむしろ定義されていない中間クラスを介して、それを行うにはいくつかの方法が必要です。

+2

デコレータを見てください。 – utdemir

+0

私たちはメソッドを使って関数やクラスについて話していますか? – tjollans

+0

私はデコレータについて知っています。私は何を飾るのですか? createPizzaとcreateBurgerはその側の関数です。 – Nathan

答えて

3

私はこのように動作しますdataCreatorアダプタクラスを作成します。

  1. デバッグ/タイミング機能にラップする必要がdataCreatorからのメソッドのmethods2wrapリストを持っています。
  2. dataCreatorメソッドに1:1をマップし、methods2wrapのメソッドをタイミングデバッグメッセージにラップするオーバーライドされた__getattribute__()を持っています。

プルーフオブコンセプトコード(例えば、クラスlistをラップし、その方法append周りデバッグタイムスタンプを挿入します)。この例で構築し、初期化時に、あなたがラップするとどのような方法が計時されるべきものをクラスに設定できるように、それはパラメトリック作ることができる。もちろん、

import time 

class wrapper(list): 

    def __getattribute__(self, name): 
     TO_OVERRIDE = ['append'] 
     if name in TO_OVERRIDE: 
      start = time.clock() 
     ret = super(list, self).__getattribute__(name) 
     if name in TO_OVERRIDE: 
      stop = time.clock() 
      print "It took %s seconds to perform %s" % (str(stop-start), name) 
     return ret 

profiled_list = wrapper('abc') 
print profiled_list 
profiled_list.append('d') 
print profiled_list 
profiled_list.pop() 
print profiled_list 

...

EDITは:あることに注意してくださいTO_OVERRIDE__getattribute__コールごとに再割り当てされます。これは設計によるものです。クラス属性として作成する場合は、__getattribute__は再帰的にループします(親の__getattribute__メソッドへの明示的な呼び出しを使用して取得する必要がありますが、リストをゼロから再構築するよりも時間がかかるでしょう)。

HTH

+0

これは、返されるメソッド内の実行時間を処理していないようです。この解決法では、 'profiled_list.append( 'd')'は実際に追加する時間ではなく、appendメソッドの取得にかかった時間だけを含みます。私は、メソッドのラップバージョンを返す必要があるかもしれないと思う。 – Gattster

5

Pythonコードをプロファイリングする場合は、手動で行うのではなく、Pythonの組み込みのprofiling librariesを使用する必要があります。

+0

私は主にその1つのライブラリの呼び出しをプロファイルし、デバッグレベルのログに出力したいと思っています。プロファイリングライブラリのように見えるかもしれないので、私はそのリンクを見ていきましょう、ありがとう! – Nathan

3

なぜ引数を呼び出す単なるラッパー関数ではないのですか?

def wrapper(func, *args, **kwargs): 
    ... timing logic ... 
    response = func(*args, **kwargs) 
    ... more timing logic 
    return response 

とそれを呼び出す:あなたは関数自体を渡す

wrapper(datacreator.createPizza, arg1, arg2, kwarg1=kwarg) 

ノート、それを呼び出さずに。

+0

デコレータのように、そうですか?これはうまくいくはずですが、私が呼び出すライブラリから、関数ごとに 'wrapper(datacreator.createPizza、arg1、arg2、kwarg1 = kwarg)'のようなコードを書く必要があります。彼らは新しい機能を導入するたびに、私は別の行を追加しなければならない...それを回避する方法があるのだろうかと思います。 – Nathan

+0

@caribou - それほどではありません - Pythonデコレータは関数オブジェクトを返します。しかし、これをデコレータにするのは簡単です。 –

+0

@caribou - あなたは私の答えにコメントしていませんでしたが、それは私の解決策が取り組もうとしている問題です。もちろん、時間をかけたいメソッドを指定する必要がありますが、その名前は挿入する必要があります。 – mac

関連する問題