functools がない

Python の便利な機能の一つにデコレータ構文があります。 デコレータとは関数(Python 2.6 からはクラスも)を修飾するもので、 既存の関数(やクラス)に新しい機能や意味を付け加えます。

以下の例で @trace の部分がデコレータ構文です。 また trace() 関数自体のことをデコレータと呼びます。

def trace(f):
    def wrapper(x):
        print "enter"
        try:
            return f(x)
        finally:
            print "leave"
    return wrapper

@trace
def f(x):
    print x

実行結果

>>> f("hello")
enter
hello
leave

アスペクト指向のようなことが簡単に実現できました。

実は上記の例には少しだけ問題があって、デコレータを適用する前の関数の情報が 失われています。

>>> f.__name__
'wrapper'

これは trace() 関数がその中で定義した wrapper() をそのまま 返しているためです。 ここで元の関数 f() の情報を wrapper() にセットしてやれば 良いのですが、ちゃんとやろうとすると結構大変です。

Python 2.5 からは functools モジュールの wraps() 関数を使って 正しいデコレータを簡単に書くことができます。

from functools import wraps

def trace(f):
    @wraps(f)
    def wrapper(x):
        print "enter"
        try:
            return f(x)
        finally:
            print "leave"
    return wrapper

デコレータ定義の中でデコレータ (@wraps(f)) が使われているのが なんだか面白いですね。

Python 2.4 では、サードパーティの decorator モジュール を使って同様のことが実現できます。

from decorator import decorator

@decorator
def trace(f, x):
    print "enter"
    try:
        return f(x)
    finally:
        print "leave"

Note

残念ながら decorator の最新版である 3.4.0 は Python 2.5 以降の構文を 使用しているため Python 2.4 には対応していないようです。 代わりに以前のバージョンの 3.3.3 をインストールしてください。