このBlogは移転しました。今後は aish.dev を御覧ください。

デコレータの元関数を保存する

テスト等でデコレータのデコレート前の関数を保存する必要があるなら、こんなのはどうだろう?

import sys
def tee(f):
    sys._getframe(1).f_locals['_raw_'+f.func_name] = f
    return f

デコレートした関数オブジェクトから元の関数を取り出すのは難しいし、テスト対象のモジュールをインポートするときに一時的にデコレータを置き換える手では安定した動作は難しい。また、外部のモジュールを再利用することを考えると、デコレータ関数に手を入れるのは避けたい。

というわけで思いついたのは上のデコレータだ。こんな感じで使う:

def deco(f):
    def wrapper(*args, **kwargs):
        print "wrapper", f
        return f(*args, **kwargs)
    return wrapper

@deco
@tee
def spam():
    print "spam"

spam()
_raw_spam()

class Ham(object):
    @deco
    @tee
    def egg(self):
        print "egg"

Ham().egg()
Ham()._raw_egg()

一番最初に実行されるデコレータとして、teeを指定しておくと、その関数オブジェクトを_raw_xxxxという名前で同じスコープに登録するようになっている。テスト等でデコレータ無しで実行したい場合には、この_raw_xxxxを使うことができる。

まあ、テスト用のコードをこういう形で本体のソースに追加するのもどうかと思うし、自分ではこのアイデアは使わない気がするが。