Python学習チャンネル by PyQ

Pythonのオンライン学習プラットフォームPyQのオフィシャルブログです

Pythonのデコレーターにfunctools.wrapsを使ってみましょう

f:id:kenken0326:20201119150823p:plain

こんにちは。PyQチームのtsutomuです。

今回は、functools.wrapsを設定するメリットを紹介します。

質問

PyQでデコレーターとは「関数を修飾(デコレート)する」「デコレートすることで、機能を追加したり、情報を付加できる」ことがわかりました。

最近functools.wrapsというデコレーターを知ったのですが、これはどのような効果があるのでしょうか。

回答

functools.wrapsを設定することで以下のことができます。

以下の解説では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のクエスト「デコレーターの使い方を学ぼう」の補足情報として作成しています。

Copyright ©2017- BeProud Inc. All rights reserved.