python超初級を脱出したい!④〜decoratorの理解を深める〜

Python
この記事は約6分で読めます。
Advertisements

読んで欲しい人

  • python初学者でシーケンス(listやtupleなんか)、条件分岐ぐらいまで終わった人
  • 超初級を脱出したい(自分も同様w)
  • __iter__って何?iterableとiteratorって何が違うのって人?

連載項目

  1. iteratorの理解を深める
  2. generatorの理解を深める
  3. decoratorの理解を深める
  4. 特殊メソッドに慣れる
  5. ポリモーフィズムに慣れる

目標は一旦終わった段階で以下のようなステータスになっていることです。

・pythonのチュートリアルが終わった感じのレベル
・iterater/generatorといった生成機っぽいやつにパッとみの辛さを感じない
・printしたらobj instanceが返ってくるという悲しい展開からの脱却
・特殊メソッドやプライベート変数とうに少し慣れる(呼び出しの概念が少し理解できた感じ)
・ポリモーフィズムを自分で作れる感じ(ただし、非常に簡単なやつ)

decoratorを理解すると何ができる?今回の記事のゴール

正直python単体において、decoratorは個人的にはそこまで重きをおかなくてもいいのかなぁと理解しています。ただ、今後Web開発をしていく際には、decoratorは非常に使用頻度が高いので、その将来を見据えて理解を深めていきたいと思います。

decoratorってそもそもなんなの?

decoratorとは、いわゆる装飾です。以上

・・・というわけにはいかないですよね。ざっくりいうと、”ある関数を別の関数で装飾することができる”ということなんですが、言葉で書いてもよくわからないと思います。サンプルに1個デコレータを作ってみましょう。

simple decoratorを作ってみる

そもそもdecorator自体がどう表現されるかというと、@+関数名となります。以下の場合は、@decoratorがtest関数にdecorator関数を装飾している、ということになります。

def decorator(func):
    def wrapper(*args, **kwargs):
        print('-- Decorator Start--')
        func(*args, **kwargs)
        print('-- Decorator End--')
    return wrapper

@decorator
def test():
    print('test関数部分')

if __name__== "__main__":
    test()

実際どういう形で表示されるかというと、以下のようになります。

-- Decorator Start--
test関数部分
-- Decorator End--

test関数は”test関数部分”という物を表示するだけです。それが、decorator関数にデコレートされたことにより、decorator関数が先に実行され、decorator関数ないのfunc引数で呼び出されて、test関数が実行されています。

実はこれって、デコレータを利用しなくても、以下のような形でも同様の結果になります。

def decorator(func):
    def wrapper(*args, **kwargs):
        print('-- Decorator Start--')
        func(*args, **kwargs)
        print('-- Decorator End--')
    return wrapper

#@decorator
def test():
    print('test関数部分')

a = decorator(test)

if __name__== "__main__":
    a()

先ほどと同様の結果が出ますが、やっていることといえば、いつもみている関数を関数で呼び出しているだけです。

これが意味することは何なんでしょうか?

少し別言語を触ったことがある方ならご存知の、いわゆるシンタックスシュガー(糖衣構文)です。デコレータの本質は、シンタックスシュガーでしかないと言われる所以です。

デコレータの使い道

デコレータの用途は、大きく分けて「ラッピング」と「マーキング」の二つがあると言われています。これまでみてきたのは、ラッピング(wrapperを使ってラップさせるからラッピング)でした。次は、マーキングの部分もみていきます。

マーキングとしてのデコレータ

マーキングが何を意味しているかから、始めましょう。

デコレータを利用したマーキングとは、デコレータが受け取ったオブジェクトをそのまま一旦返して(ここでは実行されてるけど、保存されるだけ)、受け取ったオブジェクトを何処かに記録しておいて、それを後から利用することになる。 

ということで、言葉にするより作成した方が早いので、実際作ってみてみましょう。

stock = []

def decorator(func):
    stock.append(func)
    return func

@decorator
def morning():
    print("morning")

@decorator
def noon():
    print("noon")

@decorator
def evening():
    print("evening")

@decorator
def night():
    print("night")

def main():
    print(stock)
    stock[0]()

if __name__ == "__main__":
    main()

morning/noon/evening/nightそれぞれが、print(“関数名”)という処理を行いますが、それ自体がすぐさま実行されているわけではなく、全てstockに一旦記録する機能を実現しているのが、マーキングとしてのdecoratorになります。

実行結果をみていきます。

[<function morning at 0x105c8f6a8>, <function noon at 0x105c8f730>, <function evening at 0x105c8fa60>, <function night at 0x105c8fae8>]
morning

1行目がstockですが、それぞれfunction morning/noon/evening/nightが格納されているのがわかります。

2行目で、1つ目を関数として呼び出すと、そのタイミングでmorningと表示されています。これが、一旦オブジェクトとして記録した物を後から呼び出しているという意味になります。

まとめ

デコレータについては、まだまだ引数取るパターン、メソッドで作成するパターン、クラスに付与するパターンなど、山ほど利用ケースはあります。generatorなどと併用することも可能です。ただ、そこまでは1回では書ききれないので、ドキュメントや検索してもらえればなと思います。

最後に一言だけ言えるのは、デコレータはあくまでもシンタックスシュガーであり、それ以上でもそれ以下でもありません。ただ、付与することにより、便利になる、というかフレームワーク上だと、@login_require(django), @app.route(flask)など色々そのフレームワーク上で動作するデコレータがあるので、ぜひデコレータが何をしているかをイメージできるようになるといいなぁと個人的には思います。

連載目次: python超初級脱出シリーズ

pythonの入門編を終えた後に、supper_beginnerクラスを脱出するためのシリーズです。unitテストやフレームワークの使い方に行く前に、少しpythonの知識を深めたい人用の記事です。

  1. python超初級を脱出したい!①〜超初級を脱出するには〜
  2. python超初級を脱出したい!②〜iteratorの理解を深める〜
  3. python超初級を脱出したい!③〜generatorの理解を深める〜
  4. python超初級を脱出したい!④〜decoratorの理解を深める〜
  5. python超初級を脱出したい!⑤〜特殊メソッド・特殊属性に慣れる〜

コメント