python黒魔法---装飾器(decorator)

5509 ワード

pythonは優雅な言語で、魔法のような使い方もあります.装飾器(decorator)は腐敗性を不思議にする技術である.最近はずっとTornadoフレームを使っていて、ずっとFlaskを忘れませんでした.Flaskは私の大好きなPythonフレームワークで、最初に惹かれたのも装飾器という文法糖(Syntactic sugar)を使ってRouterを作って、コードを甘く見せるからです.
Tornadoの中のRouterは少し平板で、Flaskの味が懐かしくて、Flaskがどのようにこの魔法を使うか知りたいです.Flaskのソースコードを読むことで、Tornadoのために装飾器Routerを実現することもできます.
もちろんPythonに触れたばかりの人は、装飾器の本質がデザインモードの装飾器モードであることを理解しやすいかもしれません.しかしPythonは@個を通じて装飾器の文法糖を実現した.本文の目的は@を神秘的にしないことです.
すべてが対象
Pythonではすべてが対象で、もちろんすべてが彼女であるわけではありません.関数もオブジェクトなので、パラメータとして渡すことができます.たとえば、次のようにします.
def say_english():
    print 'hello'

def say_chinese():
    print '  '

say_english()           # hello
say_chinese()           #   

def greet(say):
    say()

greet(say_english)      # hello
greet(say_chinese)      #   

私たちのgreet関数のパラメータは、関数オブジェクトでもあります.このパラメータオブジェクトを渡すことができます.greetを呼び出すとgreet内部で関数パラメータの呼び出しが行われます.
装飾モード
装飾モードは、その名の通り、ターゲット関数を呼び出す前に、この関数オブジェクトを装飾します.たとえば、データベースを操作する方法です.データをクエリーする前に、データベースに接続する必要があります.クエリーが終了したら、接続を切断して閉じる必要があります.通常の論理は次のとおりです.
def connect_db():
    print 'connect db'

def close_db():
    print 'close db'

def query_user():

    connect_db()
    print 'query the user'
    close_db()


query_user()            # connect db
                        # query the user
                        # close db

接続データベース(connect_db)と閉じた接続(close_db)を関数にカプセル化しました.query_Dataメソッドは、クエリの具体的な論理を実行します.このように異なるクエリー方法が必要で、クエリーの論理も一つの方法にカプセル化するだけでOkla
def query_user():
    print 'query some user'

def query_data(query):

    connect_db()
    query()
    close_db()

query_data(query_user)

クエリーの関数オブジェクトを転送し、冒頭の説明に合致するすべてがオブジェクトです.アクセサリーが完成しました.はい、簡単です.query_dataはquery_userの装飾はもちろんquery_も書けますblogなどの方法.
など、装飾関数を使用する前に、プロジェクトのコードに大量のquery_がある場合を想定します.userメソッドの呼び出し.query_を使用した場合data包装.前のqueryをuser()の場所はすべてquery_に置き換えられますdata(query_user).コードの変更を減らすにはどうすればいいですか?
私たちの出発点は前のqueryを維持するためです.user()は変更されず、実際にはquery_が呼び出されます.data(query_user).もしquery_dataが呼び出されると、関数が返されますか?たとえば、次のコードがあります.
def query_user():
    print 'query some user'

def query_data(query):
    """      ,      , query  wrapper   """
    def wrapper():
        connect_db()
        query()
        close_db()
    return wrapper
#     query_data      (       )
query_user = query_data(query_user)
#          query_user
query_user()

これで完全な装飾器が完成し、前のバージョンよりも前に書いたqueryを変更する必要はありません.userコード.キーはquery_ですdata呼び出しのとき、wrapper関数が返され、このwrapper関数はquery関数呼び出しの前後のいくつかの論理を実行します.もう一つのポイントはアクセサリーquery_を呼び出すことですdata装飾関数.
文法糖@
前のコードでは、pythonのアクセサリー構文糖@を使用できます.以下のようにします.
def query_data(query):

    def wrapper():
        connect_db()
        query()
        close_db()
    return wrapper

#    @          
@query_data
def query_user():
    print 'query some user'

query_user()

前の装飾器が装飾を呼び出すとpythonに文法糖があります.装飾関数の前に@を追加すると、@query_dataquery_data()に等しいいくつかの装飾関数を呼び出すと理解できる.実際には、このように使用するのではなく、このような全体です.
@query_data
def query_user():
    print 'query some user'

に等しい
query_user = query_data(query_user)

被装飾関数パラメータ
私たちが装飾された関数は、パラメータを持つことが多いので、装飾器を通じてどのようにパラメータを伝達しますか?回想すると,装飾器関数は装飾された関数に対して装飾され,wrapper関数を返すのが用いられる.実はこの関数は装飾された関数に等しいが、wrapperはもっと多くのことをしただけだ.装飾された関数パラメータはwrapperによって伝達できる.次のようになります.
def query_data(query):

    def wrapper(count):
        connect_db()
        query(count)
        close_db()
    return wrapper

@query_data
def query_user(count):
    print 'query some user limit  {count}'.format(count=count)

query_user(count=100)       # connect db
                            # query some user limit  100
                            # close db

これにより,装飾された関数伝達パラメータが実現される.もちろん、位置パラメータもキーワードパラメータも可変パラメータも可能です.
装飾パラメータ
flaskでは、ビュー関数の装飾は、装飾器にurl正則を伝達する、すなわち、装飾器にパラメータを伝達する、被装飾器のパラメータとは異なる.
@app.router('/user')
def user_page():
    return 'user page'

私たちはどのようにrouterという装飾器を定義しますか?実は元の装飾器の外にもう1階包むだけで、つまり装飾器に対して装飾を行います.
def router(url):

    print 'router invoke url', url

    def query_data(query):

        print 'query_data invoke url', url

        def wrapper(count):
            connect_db()
            query(count)
            close_db()
        return wrapper
    return query_data

@router('/user')          #      router  ,    router invoke url /user,   @  ,   'query_data invoke url', url
def query_user(count):
    print 'query some user limit  {count}'.format(count=count)

query_user(count=100)   # connect db
                        # query some user limit  100
                        # close db

@router()この文法糖は迷っているように見えますが、実はよく理解しています.ここでは、2つのステップの最初のステップがrouterという関数を呼び出すことと見なすことができます.
 query_data =  router('/user')

ステップ2では、装飾を行います.
@query_data
def query_user():
    pass

つながった効果は
query_user = query_data(query_user))

 

query_user = router("/user")(query_user))

今思えば、@という文法糖は甘いでしょう.そしてpythonと同じようによく理解され、よく使われています.
もちろん、装飾器を使用するのは、前述したflaskのrouterのような包装が必要な方法を実現するためです.
素晴らしいTutorial:Things which aren't magic-Flask and@appを書いた人がいます.routeは、装飾の確定理解を深めることを参考にすることができ、装飾器には多くの用途がある.
Enjoy~