技術

手抜きをするデコレータ

この記事に関連するかもしれない記事

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__の中で画一的な方法で取り出せないっぽい。)

コメント

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください