アルパカログ

Webエンジニア兼マネージャーがプログラミングやマネジメント、読んだ本のまとめを中心に書いてます。

Python デコレータとは?使い方を解説(decorator)

f:id:otoyo0122:20200814121441p:plain:w300

Pythonには「デコレータ」(decorator) という機能が標準で提供されています。

デコレータはPython以外のプログラミング言語ではあまり見ないので、少々とっつきにくい印象を受けます。

このエントリでは、Pythonのデコレータについて説明し、その使い方を紹介します。

デコレータとは?

デコレータとは「関数を受け取って関数を返す関数」です。

f:id:otoyo0122:20200814112131p:plain:w300
関数fは関数gを受け取って関数g'を返している

プログラミングにおいては「関数を受け取る関数」や「関数を返す関数」のことを高階関数と言います。

コードでデコレータの例を見てみましょう。

関数を返す関数

Pythonでデコレータを定義するにはfunctoolsからwraps関数をインポートします。

from functools import wraps

def to_john(f):
    @wraps(f)
    def wrapper():
        return f() + ' John!'
    return wrapper

@to_john
def say_hello():
    return 'Hello!'

上記の例で、to_john()関数がデコレータです。

to_john()関数は引数fを受け取り、内部で定義したwrapper()関数を返しています。

wrapper()関数は、引数ff()のようにして関数呼び出しし、f()の戻り値と' John!'と結合した結果を返しています。

このことから、デコレータの条件のうち「関数を返す関数」であるというのはわかりました。

しかしデコレータは「関数を受け取る関数」でもあります。

「受け取る関数」はどこで定義しているのでしょうか?

関数を受け取る関数

デコレータを使って関数をデコレート(decorate/修飾)するには、修飾したい関数の上に@decoratorのように表記します。

@to_john
def say_hello():
    return 'Hello!'

例では、デコレータ@to_johnsay_hello()関数をデコレートしています。

つまり、デコレータto_john(f)の引数fの正体は、デコレートされる関数(この場合はsay_hello())であるというわけです。

>>> say_hello()
Hello! John!

ところで@wraps(f)は何をしているのでしょうか?

wrapsデコレータは、内部的にデコレータ関数(wrapper関数)がデコレートされる側の関数(wrapped関数)に見えるようにしています。

これは、デコレータ関数でエラーが発生した場合などに役立ちます。

詳しくは下記をご覧ください。

以上です。

参考になった方は、ぜひ「はてブ」やSNSでシェアしていただけると嬉しいです。