Python デコレータを学ぶ 1

Python で関数に機能を追加する仕組みとしてデコレータというものがあります。Flask のチュートリアル で @ を使ったシンタックスがあって、どういう意味か調べるとこれがデコレータというものでした。

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

ある関数をラップして、その関数にもともと定義されている振る舞いに別な動作を追加することができます。ラッパーの部分は別途定義されているので、動作を追加したい対象の関数が複数あっても記述が煩雑になりません。

例えば以下の様な関数を定義して実行してみます。

def sandwich(food='--ham--'):
    print(food)


sandwich()

実行結果は以下のようになります。

$ python decolate.py

--ham--

これに bread() というデコレータを定義して sandwich() をデコレートしてみます。デコレータは関数を引数に取り、処理を追加した上で関数を返す関数です。(わかりにくいですね)

def bread(func):
    def wrapper():
        print('</"""""""\>')
        func()
        print('<\_______/>')
    return wrapper


@bread
def sandwich(food='--ham--'):
    print(food)


sandwich()

すると実行結果はこうなります。

$ python decolate.py

</"""""""\>
--ham--
<\_______/>

複数のデコレータを適用する

デコレータはひとつの関数に対して複数適用することもできます。

def ingredients(func):
    def wrapper():
        print('#tomatoes#')
        func()
        print('-salad-')
    return wrapper

こういう関数を定義して、先ほどの sandwich() をデコレートします。

@bread
@ingredients
def sandwich(food='--ham--'):
    print(food)


sandwich()

すると、こうなります。

$ python decolate.py

</"""""""\>
#tomatoes#
--ham--
-salad-
<\_______/>

複数重ねた場合、一番下のデコレータから順に適用されていくので順番を変えると、結果も変わってきます。

@ingredients
@bread
def sandwich(food='--ham--'):
    print(food)


sandwich()

bread() を下にすると、

$ python decolate.py

#tomatoes#
</"""""""\>
--ham--
<\_______/>
-salad-

こうなります。