【Python入門】デコレータの基本をわかりやすく解説

具体例を使ってPythonのデコレータの基本をわかりやすく解説

今回はPythonにおけるデコレーターの基本を解説します。

前提知識

デコレータの基本を理解するためには、関数のネスト(入れ子状態になった関数)についての知識がとても役立ちます。

そこで、関数のネストについて解説した過去記事「Pythonでの関数のネスト(入れ子)についての解説。デコレーターを理解するための前提」を読んでおいてください。

デコレータとは?

公式の定義

公式ドキュメントの用語集には次のような説明があります。

(デコレータ) 別の関数を返す関数で、通常、 @wrapper 構文で関数変換として適用されます

Python公式ドキュメント:用語集

大事なことは、別の関数を返す関数ということですね。この点の理解に上の前提知識が役立ちます。記事「Pythonでの関数のネスト(入れ子)についての解説。デコレーターを理解するための前提」を参照してください。

デコレータ関数の引数は、ある関数の関数オブジェクトただそれだけです。

別の関数を返す関数とは?

別の関数を返す関数については、コードとして簡略すると以下のような形になるということです。

def 関数A():

  return 関数B  # 関数そのものを返す。関数を実行した結果ではない。

これは、「関数Aとは、関数Bを返す関数」という意味です。これがどういうことなのか次のコード例を見てください。

def fnc_a():
    return fnc_b  #別の関数fnc_bを返している。fnc_b()ではない点に注意

def fnc_b():
    print('Hello')

fnc_a()  # 結果:<function fnc_b at 0x000001491F6601F0>
fnc_a()()  # 結果:2

これの処理の流れを簡単に説明すると、次のようになります。

まず関数fnc_aを実行すると結果としてfnc_bが返ってきます。「fnc_b()」にはなっていない点に注意してください。関数名の後に()をつけると、それは「関数を実行した結果」になるので、「関数」になりません。

大雑把に言うと、fnc_a()は関数fnc_bと同じになる(実際はちょっと違います)。

fnc_a()()は、fnc_b()と同じことになる。

よって文字列Helloが表示される。

ということです。

デコレータのイメージ

そしてデコレータの働きのイメージは次のような感じです。

デコレータを使うことで、ある関数の処理を「その関数をいじることなく、一時的に変更」することができる

ということです。

では以上のことを踏まえて、デコレータの基本的な使い方を見ていきましょう。

使い方とその例

使用例

# デコレータとなる関数dcrtrを定義(関数オブジェクトを唯一の引数にとる)
def dcrtr(target_fnc):
    def fnc_1(a, b):
        return target_fnc(a, b)+3
    
    return fnc_1  # fnc_1の実行結果ではなく、fnc_1という関数そのものを返す

# 変更させたい関数の「直前」に@デコレータ関数名
@dcrtr
def add(x, y):
    return x*y

add(1, 2)  # 結果:5

解説

さてこのコードを解説していきます。

def dcrtr(target_fnc):

デコレータ関数のこの部分は、通常の関数と仮引数の関係とはちょっと意味合いが異なり、

dcrtr = target_fnc

と同じ意味になります。この結果、大雑把にいうとdcrtrはtarget_fnc、つまりここでは関数addに変化します。

return fnc_1  # fnc_1の実行結果ではなく、fnc_1という関数そのものを返す

そしてこのdef dcrtr内にあるこのreturn文によって、デコレータ関数dcrtrつまり関数addは、関数fnc_1へと変化します。

では、その関数fnc_1はどんなものかといえば、

    def fnc_1(a, b):
        return target_fnc(a, b)+3

target_fncとは関数addのことでしたから、結局のところ、

add(a, b)+3

の結果が返されることになります。

今回のこの動きを画像にまとめてみました。

Pythonのデコレータの基本の解説
Pythonのデコレータの動き

この画像において、

  • dcrtr=add
  • dcrtr=fnc_1

の2つによって

add=fnc_1、つまり、関数addが関数fnc_1へと変化していることがポイントです。

実際にはどこにも、add=fnc_1という代入式は存在しないのですが、そのような結果になっています。まさにこれがデコレータの働きなんですね。

次に、デコレータを有効にした場合とそれを無効にした場合との実行結果の違いを見ておきましょう。

次の画像を見てください。

Pythonのデコレータの基本の解説
Pythonのデコレータの基本の解説

この画像ではコメントアウトによって、

  • 画像←側:デコレータを無効にした場合
  • 画像→側:デコレータを有効にした場合

と分けています。

そして実行結果は、

  • 無効にした場合→1×2=2
  • 有効にした場合→1×2+3=5

このように異なります。

デコレータを使うことで、関数addの処理を「関数addを直接いじることなく、一時的に変更」することができる、とわかります。

変更対象となる関数addをいっさいいじっていないため、関数addを再びコード内で使っても問題が生じないのですね。

あくまで「@デコレータ関数名」を直前につけた場合にだけ、変更の対象となる関数が変更されるのです。必要なときにだけ一時的に変更することが可能です。

プログラミングを学びIT業界へ転職するなら現役エンジニアから学べるプログラミングスクールTechAcademy [テックアカデミー]でオンライン講座を受講するのが良いと思います。1人で悩みながら学習を進めるよりもわかりやすく、費やす時間も少なく合理的・効率的に学習できるからです。

など各種の講座が用意されています。無料で体験できるテックアカデミー無料体験も用意されています。

3 Comments

Comments are closed.