デコレータは何も関数である必要はないわけで
オブジェクトのメソッドも形を整えれば、デコレータとしてつかえる。副次的な機能を追加するのに使えそう。
例(平均を計算するカウンタのメソッドをデコレータとして利用)
平均を返すカウンタを使った例。
f()を呼ぶと共に値はカウントされ、g()を呼ぶともにカウンタの値はリセットされる。
class AverageCounter(object): def __init__(self, convert = lambda x : x): self.initialize() self.convert = convert def initialize(self, *args, **kw): self.i = 0 self.total = 0.0 def count(self, n): self.i += 1 self.total += self.convert(n) def average(self): return self.total / self.i def method_to_decorator(method): def _method_to_decorator(fn): def __method_to_decorator(*args, **kw): method(*args, **kw) return fn(*args, **kw) return __method_to_decorator return _method_to_decorator ac = AverageCounter(convert = len) @method_to_decorator(ac.count) def f(string): return string @method_to_decorator(ac.initialize) def g(string): return string print "ac.total = %s" % ac.total print "call f: arg=%s" % f("countup countup ...") print "ac.total = %s" % ac.total print "call f: arg=%s" % f("tick tick") print "ac.total = %s" % ac.total print "i:%d total:%d average:%f" % (ac.i, ac.total, ac.average()) print "call g: arg = %s " % g("ac is initialize")
結果
ac.total = 0.0 call f: arg=countup countup ... ac.total = 19.0 call f: arg=tick tick ac.total = 28.0 i:2 total:28 average:14.000000 call g: arg = ac is initialize ac.total = 0.0
まだ使い方が整理できていないけれど。。
実際に使う場合には、クラスを作りながら使うことになりそう。。
__init__でデコレータに利用するオブジェクトをを初期化してインスタンス変数として持っておく。
f, gの代わりにメソッドをデコレータで装飾することになりそう。
(メソッドの中で、明示的にメソッドを呼ぶこととの違いは?)
何かの処理に付随して、他の方向の副次的な処理をさせたい時に使えそう。
メソッドの本体には実際に必要な処理だけを書くことができて便利。
状態を共有しているので、1)数える。2)初期化するという別の動作を行わせることができるのが、関数としてデコレータを使った場合との違い。