理系学生日記

おまえはいつまで学生気分なのか

関数デコレータによる print デバッグ

最近ようやく Python を本格的に触りはじめて、なかなかおもしろいなーという感じになってる。イテレータとかジェネレータ、メタクラスとかが言語と密に関連していて、オシャレなかんじ。そういうわけで、今後いろいろと Python まわりの初心者っぽいアウトプットを出していきたいとおもいます。

よくあるケースとして、デバッグするときに関数の戻り値を確認したいということがあります。

def twice(x):
    return x * 2

漢は黙って print デバッグ、みたいなことをしようと思うと、こんなかんじになる。一々書きかえないといけないの、マジでダルい。

def twice(x):
    ret = x * 2
    print("ret={}".format(ret))

なんか楽な方法ないかなーとか思ってたら関数デコレータがあった。

関数デコレータを使って print デバッグ

関数デコレータ、単なるシンタックスシュガーなんだけど、別の関数を返す関数をカッコよく定義できる。たとえば、さきほどの print デバッグに使えるデコレータは以下のように書ける。

def debug(f):
    def wrapper(*args, **kwds):
        "for print debug"
        ret = f(*args, **kwds)
        print("ret={}".format(ret))
        return ret
    return wrapper    

になる。
関数 f を引数として受けとって wrapper という関数を返却する。じゃぁ wrapper は何をするのかというと、f を呼び出して、その結果を print しつつ、結果を return する。

これを使えば、twice をデバッグしたい、ということになった場合、@debug で twice を修飾(decorate)するだけで print デバッグが可能になる。

@debug
def twice(x):
    return x * 2

f(10)
# prints "ret=20"

上の方法の欠点

f(10) という呼び出しにおける f の実体は、wrapper になってる。これは以下のようにして確認できる。

print(twice.__name__)
# prints "wrapper"
print(twice.__doc__)
# prints "for print debug"

あたかも twice であるかのように見せるためには、functools.wraps を使用すれば良い。

from functools import wraps

def debug(f):
    @wraps(f)
    def wrapper(*args, **kwds):
        "for print debug"
        ret = f(*args, **kwds)
        print("ret={}".format(ret))
        return ret
    return wrapper

@debug
def twice(x):
    return x * 2

print(twice.__name__)
# prints "twice"
print(twice.__doc__)
# prints "None"