Pythonデコレーションを理解する

3127 ワード

Pythonデコレーションを理解する
Python装飾器はJavaの注釈に似ているように見えますが、ガチョウと注釈は違っていますが、同様に切断面向けのプログラミングができます。
Pythonの中の装飾器を理解するには、まずクローズドという概念を理解しなければならない。
包みを閉じる
ウィキペディアの解説を見てください。
コンピュータ科学では、クローズド(英語:Close)、またはクローズド(Lexical Cloure)または関数クローズド(function closures)は、自由変数を参照した関数である。この参照された自由変数はこの関数と共に存在し、作成された環境から離れても例外ではない。
公式の説明はいつも人の話をしません。but-talk is chap、show me the code:
# print_msg     
def print_msg():
    msg = "I'm closure"

    # printer     
    def printer():
        print(msg)

    return printer


#            
closure = print_msg()
#    I'm closure
closure()
msgは局所変数であり、print_msg関数の実行後は存在しないはずである。しかし、ネスト関数はこの変数を参照して、この局所変数をネスト関数に閉じ込めると、閉じられたパケットが形成されます。
この例を合わせてウィキペディアの説明を見れば、分かりやすくなります。クローズドとは、固有の変数の関数を参照して、この関数は実行されたコンテキストを保存しています。元のスコープから独立して存在します。
Pythonの中の飾りを見てみます。
飾り物
普通の装飾器は普通こうです。
import functools


def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('call %s():' % func.__name__)
        print('args = {}'.format(*args))
        return func(*args, **kwargs)

    return wrapper
このようにして、方法名とそのパラメータを印刷する装飾器を定義します。
呼び出し:
@log
def test(p):
    print(test.__name__ + " param: " + p)
    
test("I'm a param")
出力:
call test():
args = I'm a param
test param: I'm a param
装飾器は使う時に、@文法を使って、困っています。実は、飾り器は一つの方法で、以下の呼び方と区別がありません。
def test(p):
    print(test.__name__ + " param: " + p)

wrapper = log(test)
wrapper("I'm a param")
@文法は、関数を装飾器関数に導入するだけで、不思議なところがない。
注目すべきは@functools.wraps(func)であり、これはpythonが提供する装飾器である。元関数のメタ情報を装飾器の中のfunc関数にコピーすることができます。関数のメタ情報には、docstring、name、パラメータリストなどがあります。@functools.wraps(func)を除去してみてもいいです。test.__name__の出力はwrapperになりました。
パラメータ付き装飾器
装飾器はパラメータの入力を許可します。パラメータを持った装飾器は次のように3つの関数があります。
import functools

def log_with_param(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('call %s():' % func.__name__)
            print('args = {}'.format(*args))
            print('log_param = {}'.format(text))
            return func(*args, **kwargs)

        return wrapper

    return decorator
    
@log_with_param("param")
def test_with_param(p):
    print(test_with_param.__name__)
このコードを見てまた疑問がありますか?内部層のdecorator関数のパラメータfuncはどうやって伝えられますか?上のような装飾器とは違いますね。
本来は同じです。@文法を除いて、関数コールの形を回復して見れば分かります。
#         ,      decorator  
decorator = log_with_param("param")
#   test_with_param  
wrapper = decorator(test_with_param)
#        
wrapper("I'm a param")
出力結果は通常の飾り付けと同じです。
call test_with_param():
args = I'm a param
log_param = param
test_with_param
これでは、飾り器というやや難解な特性も神秘的ではなくなった。
装飾器という文法は、Pythonの関数が第一公民であり、関数が対象であり、変数であり、パラメータとしてもよいし、戻り値としても良いし、非常に柔軟で強力であることを表しています。
Pythonには多くの関数式プログラミングの特性が導入されています。