n回の呼び出しのうち1回しか実際の処理を行わないデコレータを考えてみた。
(Zinniaを使って手書き文字を認識するプログラムを作ってたときに、ペンの描画(マウスのドラッグ)イベントが物凄い回数起きても座標データが増大するだけで精度はあまり上がらないんじゃないかと思い、程良く処理回数を減らすため考えた。)
def negligence(n): u'''n回に1回しか処理しないデコレータ''' def _(f): f.call_count = -1 def __(*args, **kwargs): f.call_count += 1 if f.call_count % n == 0: f.call_count = 0 return f(*args, **kwargs) return __ return _
使い方
class negtest(object): @negligence(3) def test(self, text): print text @negligence(5) def nttest(text): print text nt = negtest() for i in range(10): nt.test('test%d' % i) print for i in range(15): nttest('test%d' %i) # 実行結果 test0 test3 test6 test9 test0 test5 test10
注意点
関数オブジェクトにcall_countという属性を追加してるのでバッティングする可能性がある。
処理しなかった場合の戻り値としてNoneが返ってくるため、呼び出し側でそれを考慮してないと問題が起きるかも。(戻り値を使う部分で処理を省きたかったらMemoize!)
というか、そもそも固定的に処理回数を減らしたい事ってあまり無いようなw 特にまともなプログラムになればなるほど。
失敗作
呼び出し回数という状態を持つため、最初は以下のようなクラスを返すデコレータで実装してたけど、functionの場合、bound methodの場合、unbound methodの場合、コールバックの場合のすべてで画一的な方法で正常に動作させる方法が分からず断念。
sys._getframeとか使ってみたけど、それぞれの場合でうまく動く方法があったとしても、激しく場合分けが必要な予感。
def negligence(n): u'''n回に1回しか処理しないデコレータ''' class _(object): def __init__(self, f): self.__f = f self.__call_count = -1 def __call__(self, *args, **kwargs): self.__call_count += 1 if self.__call_count % n == 0: self.__call_count = 0 return self.__f(*args, **kwargs) return _
ちなみにこの例だとfunctionにしか適用出来ない。(他の場合は元のメソッドの第一引数のselfを__call__の中で画一的な方法で取り出せないっぽい。)
コメント