(二十四)python 3装飾器

14149 ワード

python装飾器の詳細な解析
python装飾器(fuctional decorators)は、元の関数機能を拡張するための関数であり、元の関数名(またはクラス名)を変更せずに関数に新しい機能を追加することを目的としている. 
この関数の特殊な点は、戻り値も関数であり、この関数は「元の」関数を埋め込んだ関数であることです.
一般的に、元の関数コードを拡張するには、コードに侵入して修正することが最も直接的な方法です.例えば、
import time
def f():
    print("hello")
    time.sleep(1)
    print("world") 

これは私たちの最も原始的な関数で、それから私たちはこの関数の実行の総時間を記録しようとしました.その最も簡単な方法は元のコードを変更することです.
import time
def f():
    start_time = time.time()
    print("hello")
    time.sleep(1)
    print("world")
    end_time = time.time()

    execution_time = (end_time - start_time)*1000
    print("time is %d ms" %execution_time)

しかし、実際の作業では、コアコードが直接変更できない場合があるので、元のコードを変更しない場合は、関数を定義することができます.(ただし有効になるには関数を再実行する必要があります)
import time

def deco(func):
    start_time = time.time()
    f()
    end_time = time.time()
    execution_time = (end_time - start_time)*1000
    print("time is %d ms" %execution_time)

def f():
    print("hello")
    time.sleep(1)
    print("world")

if __name__ == '__main__':

    deco(f)
    print("f.__name__ is",f.__name__)
    print()

ここでは,パラメータが関数である関数decoを定義し,この関数にタイミング機能を埋め込んだ.しかしこの千万の関数機能を拡張するには
1千万回のdeco()関数を実行するので、理想的ではありません.次に、装飾器で実現してみましょう.まず、装飾器の最も原始的な姿を見てみましょう. 
import time

def deco(f):
    def wrapper():
        start_time = time.time()
        f()
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" %execution_time )
    return wrapper

@deco
def f():
    print("hello")
    time.sleep(1)
    print("world")

if __name__ == '__main__':
    f()

ここでのdeco関数は最も原始的な装飾器であり、そのパラメータは関数であり、戻り値も関数である.
ここで、パラメータであるこの関数f()は、戻り関数wrapper()の内部で実行される.そして関数f()の前に@decoを付けて、
f()関数はカウントダウン機能が注入されたものに相当し、現在はf()を呼び出すだけで「新しい機能がもっと多い」関数に変身している.
(元の関数を繰り返し実行する必要はありません). 
 
拡張1:固定パラメータ付き装飾
import time

def deco(f):
    def wrapper(a,b):
        start_time = time.time()
        f(a,b)
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" % execution_time)
    return wrapper

@deco
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))

if __name__ == '__main__':
    f(3,4)

拡張2:固定パラメータのない装飾
import time

def deco(f):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        execution_time_ = (end_time - start_time)*1000
        print("time is %d ms" %execution_time)
    return wrapper


@deco
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))

@deco
def f2(a,b,c):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b+c))


if __name__ == '__main__':
    f2(3,4,5)
    f(3,4)

拡張3:複数のアクセサリを使用して、1つの関数を装飾する
import time

def deco01(f):
    def wrapper(*args, **kwargs):
        print("this is deco01")
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" % execution_time)
        print("deco01 end here")
    return wrapper

def deco02(f):
    def wrapper(*args, **kwargs):
        print("this is deco02")
        f(*args, **kwargs)

        print("deco02 end here")
    return wrapper

@deco01
@deco02
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))


if __name__ == '__main__':
    f(3,4)
'''
this is deco01
this is deco02
hello,here is a func for add :
result is 7
deco02 end here
time is 1003 ms
deco01 end here
'''

アクセサリー呼び出し順序
アクセサリーは重ねて使えますが、アクセサリーを使った後のコードはどのような順序ですか?
Pythonの"@"構文糖の場合、装飾器の呼び出し順序は@構文糖を使用して宣言する順序とは逆です.
この例では、「f(3,4)=deco 01(deco 02(f(3,4))」である.
 
Python内蔵装飾器
Pythonには3つの内蔵装飾器があり、いずれもclassに関連しています:staticmethod、classmethod、property.
  • staticmethodはクラス静的メソッドであり、メンバーメソッドとの違いはselfパラメータがないことであり、クラスがインスタンス化されずに
  • を呼び出すことができる.
  • classmethodとメンバーメソッドの違いは、受信された最初のパラメータがself(クラスインスタンスのポインタ)ではなくcls(現在のクラスの特定のタイプ)
  • であることである.
  • propertyは属性の意味であり、クラスインスタンスを介して直接アクセス可能な情報
  • を表す.
    staticmethodとclassmethodについてはここでは紹介しませんが、一例でpropertyを見てみましょう.
    class Foo(object):
        def __init__(self,var):
            super(Foo,self).__init__()
            self._var=var
        @property
        def var(self):
            return self._var
        @var.setter
        def var(self,var):
            self._var=var
    
    
    f=Foo("a")
    print(f.var)
    f.var="b"
    print(f.var)

    なお、Pythonの新式クラス(new-style class)について、上の「@var.setter」装飾器で装飾するメンバー関数を削除すると、Foo.varプロパティは読み取り専用プロパティで、foo.var=‘var 2’)を使用して値を割り当てると例外が放出されます.ただし、Python classic classの場合、宣言された属性はread-onlyではないので、'@var.setter'デコレーションを削除してもエラーは発生しません.
    転載先:https://www.cnblogs.com/a-ant/p/11397082.html