こんにちは。PyQチームのtsutomuです。
今回は、functools.wraps
を設定するメリットを紹介します。
質問
PyQでデコレーターとは「関数を修飾(デコレート)する」「デコレートすることで、機能を追加したり、情報を付加できる」ことがわかりました。
最近
functools.wraps
というデコレーターを知ったのですが、これはどのような効果があるのでしょうか。
回答
functools.wraps
を設定することで以下のことができます。
- 関数名を適切に設定
- ドキュメンテーション文字列(docstring)を適切に設定 functools --- 高階関数と呼び出し可能オブジェクトの操作 — Python 3.9.0 ドキュメント
以下の解説ではfunctools.wraps
を設定することでどんなメリットがあるのか、もう少し掘り下げて紹介します。
解説
改良前のデコレーター
こちらは、PyQのクエスト「デコレーターの使い方を学ぼう」に出てくるデコレーターです。
def show_func_name(func): def wrapper(*args, **kwargs): print(func.__name__, 'を実行します') return func(*args, **kwargs) return wrapper
このデコレーターを使うと、関数の実行前に「(関数名)を実行します
」と表示します。
たとえば、下記を実行すると、「talk を実行します
」と表示されます。
@show_func_name def talk(message): print(message) talk('ごきげんよう')
さて、デコレーターは、いくつでも指定できます。
@show_func_name @show_func_name def talk(message): print(message) talk('ごきげんよう')
上記を実行すると、下記の出力になります。
wrapper を実行します talk を実行します ごきげんよう
show_func_name(talk)
の関数名がwrapper
になっています。
期待する動作とは違いますね。なぜでしょう?
@show_func_name
が1つのとき:talk関数の実態はwrapper
です。funcはtalkなので「talkを実行します」と表示します。
@show_func_name
が2つのとき:talk関数の実態はwrapper
です。funcもwrapper
なので「wrapper
を実行します」と表示します。
改良後のデコレーター
下記のように修正しましょう。違いは@wraps(func)
が追加されているところです。
from functools import wraps def show_func_name(func): @wraps(func) def wrapper(*args, **kwargs): print(func.__name__, 'を実行します') return func(*args, **kwargs) return wrapper @show_func_name @show_func_name def talk(message): print(message) talk('ごきげんよう')
上記のようにすれば、talk を実行します
が2回出力されます。
デコレーターfunctools.wraps
があることで、「@show_func_name
でデコレートした関数の名前」を「オリジナルの関数の名前(talk)」に設定するからです。
他にも、オリジナルのdocstringを設定してくれたりします。
簡単に使えて、かゆいところに手が届きますので、是非@wraps
を使ってみましょう。
このお悩み解決に関連するクエスト
このお悩み解決はPyQのクエスト「デコレーターの使い方を学ぼう」の補足情報として作成しています。