Pythonのデコレータと高階関数の使い方、戻り値や引数について【Python tips】

Python

独習Python [ 山田 祥寛 ]

 

こんにちは、monachan_papaです。
Pythonにはデコレータというかなり特殊な機能があります。名前から察するように、何かをデコレーションする機能です。
では、具体的にどんな機能か?そして、書き方は?じっくり見ていきましょう。

デコレータとは?

デコレータを一言で表すと、

コード改変なしで、既存の関数に処理を追加実装できる機能

この機能を使いこなせるようになると、例えば以下のようなシーンで威力を発揮することができます。

  • 関数Aがある。
  • これにログ機能を追加実装したくなった。
  • しかし、関数Aのコードは絶対に変更したくない。

上記のシーン。欲張りな要件ですね。しかし、デコレータを使えば、実現可能です。
とはいえ、デコレータは構造上、複数の関数が絡み合って実現する機能になります。
よって、以降でスッテップ・バイ・ステップで説明していきます。

デコレータ作成の前提知識

デコレータを作る前提として、以下の文法をしっかりマスターしているのであれば、理解が早くなります。
もし、そもそも理解していない、あるいは理解があやふや。という御方は、これを機にマスターすることをおすすめします。

  • 関数内関数
  • 引数としての関数
  • 戻り値としての関数
  • *args

高階関数

関数を変数として扱う。
これを利用すると、「関数に追加処理をする関数」 ができます。
以下は、関数を変数として扱ったコード例です。
流れを正確につかめるように、かなり詳細にコメント付与してあります。

実行すると、
Python
デコレータ
マスターしよう!
と出力されます。

# 「追加処理が施された関数」の引数に、
# 「ベースとなる関数」を受け取る。
def add_fn(f):

    # 「追加処理とベース処理がミックスされた関数」
    def mix_fn():
        print('Python')
        f()
        print('マスターしよう!')

    # 「追加処理とベース処理がミックスされた関数」をリターン
    return mix_fn

# 「ベースとなる関数」
def base_fn():

    # 単なるprint出力だけ
    print('デコレータ')

# 「追加処理が施された関数」の引数に、
# 「ベースとなる関数」を渡す。
#  戻り値は「追加処理とベース処理がミックスされた関数」となる。
#  これを「デコレートされた関数」に格納する。
deco_fn = add_fn(base_fn)

# 「デコレートされた関数」を実行する。
deco_fn()
Python
デコレータ
マスターしよう!

上記のコードで、ポイントとなる点があるので、列挙します。

  • base_fn()関数は、一切コード変更がされていない。
  • 関数を変数として扱っているところは、関数名の後ろの括弧()なし。
  • add_fn()関数のように、引数や戻り値に関数を含んでいるものを高階関数と呼ぶ。

これらを理解した上で、次に進みます。

 


独習Python [ 山田 祥寛 ]

高階関数を呼び出すデコレータ

基本形

add_dn()関数は高階関数であり、引数や戻り値に関数を含んでいました。
この高階関数をもっと簡単に呼び出す仕組みがあれば、とても楽です。
そうです。それを仕組み化し、関数に対し、常に指定した高階関数を呼び出すのがデコレータなのです。

構文はとても簡単で、デコレートされる関数定義の上に、@高階関数名を書くだけです。

@高階関数名
def デコレートされる関数名():
    # 処理

さきほどのコードを、デコレータを使って書き換えてみます。

def add_fn(f):
    def mix_fn():
        print('Python')
        f()
        print('マスターしよう!')

    return mix_fn

@add_fn
def base_fn():
    print('デコレータ')

base_fn()
Python
デコレータ
マスターしよう!

とてもシンプルになりましたね。これはかなりの省力化です。
しかし、コードの流れが初心者には追いづらくなってします、というデメリットがあります。

デコレートされる関数に引数や戻り値がある場合

さきほどのコードでは、デコレートされる関数base_fn()には引数がありませんでした。また、戻り値もありませんでした。

もし、デコレートされる関数に引数や戻り値があった場合は、あのコードのままだと使えません。
しかし、可変長引数と戻り値の設定をすることにより、デコレータが使えるようになります。

以下は、デコレートされる関数に引数や戻り値がある場合の対応方法です。

def add_fn(f):
    def mix_fn(*args):
        print('Python')
        print('マスターしよう!')
        res = f(*args)
        return res
    return mix_fn

@add_fn
def base_fn(a, b):
    return a + b

res = base_fn('デコレータ', 'も、そろそろね。')
print(res)
Python
マスターしよう!
デコレータも、そろそろね。

以上で、デコレータについての説明を終わります。
デコレータは知らなくても、何とかなる機能ではあります。特に初心者のうちは、学習効率という観点から初めはスルーで良いと考えます。なぜなら、他にもっと優先してマスターしなければならない内容があるからです。
しかし、いつかはマスターしたいですよね。Python使いならば。

また、冒頭でも述べたように、デコレータは前提知識自体が重要で価値あるものなので、学習すると本当にためになりますよ。
独習Python [ 山田 祥寛 ]では、デコレータの構造を詳細な図入りで、さらに詳しく丁寧に解説しています。「関数内関数、引数としての関数、戻り値としての関数、*args」についてもきっちり解説されているので、大変おすすめです。


独習Python [ 山田 祥寛 ]

 

また、爆速で デコレータを含めPython文法 を学びたい御方にはプログラミングスクールを考えるのも、ひとつの手です。独学よりも効果が出やすいですが、いかんせん投資がけっこうかかります。しかし、techgymというスクールは通うか通わないかは別として、無料のサンプルテキスト&解説動画がもらえます。これをとりあえず getしてまずは試しに体験学習するのもありでしょう。

コメント

タイトルとURLをコピーしました