lispとrubyとpythonと その7 デコレータ構文(pythonだけ)

python2.4から入ったデコレータ構文。
単純なシンタックスシュガーで動作も理解しやすいけど、色々使いどころはありそう。
面白い。

#デコレータ構文

#ここの説明が一番分かりやすかった
#http://morchin.sakura.ne.jp/effective_python/decorator.html

#C#でいうとアトリビュート、javaでいうアノテーションのPythonでの方法。
#実装内容で見れば全然違うけど、シンタックス上は似てるし、AOPっぽい。

#関数のトレースを出力するデコレータfunc_traceがこんな感じ
import datetime
import functools

    
def func_trace(f):

    @functools.wraps(f) #fの属性をwrapperにコピーするためのおまじない。wrapper関数をデコレートしてる。
    def wrapper(*args, **kw):
        print("start:" + datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ":" + f.__name__)
        rtn = f(*args,**kw)
        print("end:" + datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ":" + f.__name__)
        return rtn
    return wrapper

@func_trace
def foo ():
    print("hello")


#引数をとるデコレータ
#ファイル名をとって、そのファイルに呼び出し履歴をロギングする
def func_log(filnm):

    def deco(f):
        @functools.wraps(f) #fの属性をwrapperにコピーするためのおまじない。wrapper関数をデコレートしてる。
        def wrapper(*args, **kw):
            with open(filnm,"w") as fs:
                fs.write("start:" + datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ":" + f.__name__ + "\n")
                rtn = f(*args,**kw)
                fs.write("end:" + datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ":" + f.__name__ + "\n")
            return rtn
        return wrapper
    return deco

@func_log(".//test.log")
def woo ():
    print ("hello")

#Pythonのデコーレータ構文はタダのシンタックスシュガー
#まず引数をとるデコレータは忘れておく
#@xxx
#def foo ():
#これは
#foo = xxx(foo) 
#と同じ意味。
#xxxは関数を引数にとる関数
#上記の例だとfooがxxxの引数に渡ってくる。
# なのでxxxでfooの前後に処理をしつつfooを呼び出す関数を返すのが一般的

# 引数をとるデコレータ
# @xxx(1,2,3)
# def foo():
# は
# foo = xxx(1,2,3)(foo)
# と同じ意味
# つまり
# f = xxx(1,2,3) #fは関数をとって関数を返す関数
# foo = f(foo)
# こういうこと
# デコレータは複数指定できる
# @aaa
# @bbb
# @ccc
# def foo():
# だったら
# f = aaa(bbb(ccc(f)))
# と同じ意味になる
# 適用順序に注意