2020年6月11日木曜日

Python デコレータ入門

概要

Python のデコレータは関数の前後に処理を入れたり関数の引数のチェックなどが行なえます
今回は自作のデコレータを作成して挙動を確認してみました

環境

  • macOS 10.15.5
  • Python 3.8.3

最初のステップ

まずはデコレータの雛形を作成します
デコレータ自体は特に何もしていないため test メソッドの内容がそのまま出力されます

  • vim 1.py
def first_deco(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
    return wrapper

@first_deco
def test():
    print("hello")

test()
  • python 1.py

=> hello

func(*args, **kwargs) が test メソッド本体になります
なのでこの前後に何か処理を書くことで前後の処理を定義できます

デコレータに引数をつける

次にデレコータに引数をもたせてみます
一つ階層を上げてまず引数を受け取る関数を定義するのがポイントです
今回はデコレータに指定されたメッセージを本体の関数がコールされたあとに表示するようにしています

  • vim 2.py
def first_deco(msg):
    def _first_deco(func):
        def wrapper(*args, **kwargs):
            func(*args, **kwargs)
            print(msg) # 本体の関数のあとに msg を表示する
        return wrapper
    return _first_deco

@first_deco(msg="world")
def test():
    print("hello")

test()

=> hello world

デコレータを付与した関数の引数を扱う

デコレータを付与した関数をデコレータ内で扱ってみます
関数の引数は args と kwargs にそのまま入ってきます

  • vim 3.py
def first_deco(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print(args[0])
        print(kwargs["post_msg"])
    return wrapper

@first_deco
def test(msg, post_msg=""):
    print("hello")

test("world", post_msg="!!")
# test("world") => KeyError: 'post_msg'

=> hello world !!

kwargs は本体の関数を呼び出した際に指定がないとデコレータ側には届かないようです
要するにで kwargs のデフォルト値は関数本体にしか影響を与えないようです

レスポンスがある場合

関数本体に return がある場合はデコレータ内でもちゃんと return を書く必要があります

  • vim 4.py
def first_deco(msg):
    def _first_deco(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs) + msg
        return wrapper
    return _first_deco

@first_deco(msg="world")
def test():
    return "hello"

print(test())

=> hello world

普通の関数としても使える

デコレータとして関数に付与しなくても普通の関数として使うこともできます
first_deco にキーワード引数を与えてその戻り値に関数本体である test を渡して最後に test に必要な引数を与えています 

  • vim 5.py
def first_deco(msg):
    def _first_deco(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs) + msg
        return wrapper
    return _first_deco

def test():
    return "hello"

print(first_deco(msg="world")(test)())

=> hello world

最後に

Python のデコレータの機能を使ってみました
とりあえずは関数の前後処理を実装するためのものだと思っておけば良さそうです
調べるといろいろとサンプルコードが出てくるのでデコレータを作ること自体そんなに大変ではなりですが実際に手を動かしてサンプルを作ってみることでより理解が深まるかなと思います

参考サイト

0 件のコメント:

コメントを投稿