前回の投稿からの続きです。
関数内関数定義を使う
前回までは、デコレータの記述に無名関数を使用していました。 Pythonは関数内で関数を定義することができるので、無理に無名関数を使わずに、 定義した関数を返したほうが、可読性が良くなる場合があります。
# 無名関数デコレータ
>>> def dec00(a_func):
... return (lambda *args,**kwds: a_func(*args,**kwds))
...
# 関数内関数定義デコレータ
>>> def dec01(a_func):
... def wrapper(*args, **kwds):
... return a_func(*args, **kwds)
... return wrapper
...
# どちらも、渡された関数を呼び出しているだけ
関数内関数定義デコレータの返す関数の名前は、慣習的に'wrapper'とされるようです。
functoolsを使う
Pythonは内省的なので、関数の名前を実行時に取得できます。
>>> def hoge():
... return 'hoge'
...
# '__name__'に名前が入っています
>>> hoge.__name__
'hoge'
デコレータによって、この名前は上書きされてしまいます。
>>> dec00(hoge).__name__ # 無名関数デコレータ
'<lambda>'
>>> dec01(hoge).__name__ # 関数内関数定義デコレータ
'wrapper'
標準ライブラリfunctools.wrapsを使うことで、これを修正できます。
import functools
# 無名関数デコレータ
>>> def dec00(a_func):
... return functools.wraps(a_func)(lambda *args,**kwds:a_func(*args,**kwds))
...
>>> dec00(hoge).__name__
'hoge'
# 関数内関数定義デコレータ
>>> def dec01(a_func):
... @functools.wraps(a_func)
... def wrapper(*args, **kwds):
... return a_func(*args, **kwds)
... return wrapper
...
>>> dec01(hoge).__name__
'hoge'
functools.wrapsの引数に元の関数を渡していて、 functools.wraps自体がデコレータとして適用できます。
デコレータのカスタマイズ
functools.wrapsのようにデコレータ自体に引数を渡して振舞を変更したい場合、 デコレータを返す関数として定義することになります。
>>> def dec02(a_name):
... def decorator(a_func):
... @functools.wraps(a_func)
... def wrapper(*args,**kwds):
... return a_name + a_func(*args,**kwds)
... return wrapper
... return decorator
...
# a_name = 'dec02'としてデコレータを作成し、fugaに適用
>>> @dec02('dec02')
... def fuga(a_msg):
... return a_msg
...
>>> fuga('fuga')
'dec02fuga'
"'関数を受け取り関数を返す関数'を返す関数"です。 ……字面を考えると解らなくなりますね。
──続きます。
0 件のコメント:
コメントを投稿