デコレータ

Pythonにはデコレータ構文がある。
関数を他の関数で包んで、機能を追加するのが(使う分には)簡単にできる構文だ。
TurboGearsでは、テンプレートの指定や入力チェック、認証チェックなどをデコレータで行うようになっている。
詳しい説明は以下の資料で。
http://www.nasuinfo.or.jp/FreeSpace/kenji/sf/python/virtualMachine/decorator.htm

デコレータを実際に作る方法も上記資料で詳しく述べられているが、Python2.5から使えるfunctools.partialを使った方法を検討してみた。

まず、クロージャだけで実現させる例。
二重にクロージャ書くのがだるい。っていうか分かりにくい
関数をうけとって関数を返す関数を返す?

def trace(before, after):
    def decorate(func):
        def trace_func(*args, **kwargs):
            before()
            result = func(*args, **kwargs)
            after()
            return result
        return trace_func
    return decorate

def before():
    print "before"

def after():
    print "after"

@trace(before, after)
def hello():
    print "hello"

呼び出し可能オブジェクトを使った例。
引数を受け付けるのがコンストラクタに移るので、クロージャが一段減る。

class Trace(object):
    def __init__(self, before, after):
        self.before = before
        self.after = after

    def __call__(self, func):
        def trace(*args, **kwargs):
            self.before()
            result = func(*args, **kwargs)
            self.after()
            return result
        return trace

def before():
    print "before"

def after():
    print "after"

@Trace(before, after)
def hello():
    print "hello"

パーシャル関数を使った例
引数設定をパーシャルで固定して、ラップする関数を引数とした関数にする。
デコレータの書き方が上のやつと変わってしまうのが難点。

from functools import partial

def trace(before, after, func):
    def trace(*args, **kwargs):
        before()
        result = func(*args, **kwargs)
        after()
        return result
    return trace

def before():
    print "before"

def after():
    print "after"

@partial(trace, before, after)
def hello():
    print "hello"

もう少し検討すれば、paritalの恩恵を受けつつ通常と同じ書き方ができそうな気がするのだが。