python関数プログラミング-アクセサリーdecorator
10943 ワード
関数はオブジェクトであり、変数に値を割り当てることができます.変数によって関数を呼び出すこともできます.
関数を使用する_name_関数の名前を取得できます.
関数now()を呼び出す前後にログを自動的に印刷するが、now()関数の定義を変更することは許されない場合、コードの実行中に機能を動的に追加する方法を「装飾器」Decoratorと呼ぶ.
たとえば、ログを印刷できるdecoratorを定義します.
log関数を観察すると,本質的にはこれが関数を返す高次関数であることが分かった.logはdecoratorとして、パラメータとして関数を受信し、氷ご飯と関数を受信します.pythonの@構文を使用して、decoratorを関数の定義の場所に配置します.
now()関数を呼び出すと、now関数だけでなく、その前にログが印刷されます.
実は,@logをnow()関数の定義の前に置くと,実行に相当する.
now = log(now)
logはdecoratorで、1つの関数を返して、返したこの関数の名前はwrapperで、元のnow()関数はまだ存在して、この時now変数はこの返し関数wrapperを指しました.now()を呼び出すと、新しい関数wrapper関数が実行されます.wrapper関数のパラメータは(*args,**kw)なので、wrapper()関数は任意のパラメータを受け入れることができます!wrapper関数の内部では、まずログを印刷し、元の関数を呼び出します.
分割線------------------------------------
decorator自体がパラメータを入力する必要がある場合は、decoratorを返す高次関数を記述する必要があります.たとえば、logのテキスト、定義、使用方法、および結果をカスタマイズします.
前の例では2層のdefネストが含まれており、後の例では3層のdefネストが含まれている.実際には、3つのネストの効果は似ています.
now=log('実行')(now)
解析:まずlog('実行')を実行し、decorator関数を返し、戻り関数を呼び出します.パラメータはnow関数で、最終的な戻り値はwrapper関数です.
ただし、次の文を実行してテストします.
decorator装飾後の関数はname__属性はnowからwrapperに変わりました.これは、返されるその関数wrapper関数の名前がwrapperであるため、元の関数の__をname__プロパティはwrapper関数にコピーされます.そうしないと、他の関数署名に依存するコードの実行がエラーになります.
実際にはwrapperを書く必要はありません.name__ = func.__name__このようなコードはpythonにfunctoolsを内蔵しています.wrapsはそのためです.最も
最後のステップでは、完全なdecoratorの書き方は以下の通りです.
パラメータ付きdecoratorの場合:
例:任意の関数に作用し、その関数の実行時間を印刷するdecoratorを設計します.
>>> def now():
... print('2017-12-28')
...
>>> l = now
>>> l()
2017-12-28
関数を使用する_name_関数の名前を取得できます.
>>> now.__name__
'now'
>>> l.__name__
'now'
関数now()を呼び出す前後にログを自動的に印刷するが、now()関数の定義を変更することは許されない場合、コードの実行中に機能を動的に追加する方法を「装飾器」Decoratorと呼ぶ.
たとえば、ログを印刷できるdecoratorを定義します.
>>> def log(func):
... def wrapper(*args,**kw):
... print('call %s():' % func.__name__)
... return func(*args,**kw)
... return wrapper
...
log関数を観察すると,本質的にはこれが関数を返す高次関数であることが分かった.logはdecoratorとして、パラメータとして関数を受信し、氷ご飯と関数を受信します.pythonの@構文を使用して、decoratorを関数の定義の場所に配置します.
>>> @log
... def now():
... print('2017-12-28')
...
>>> now()
call now():
2017-12-28
now()関数を呼び出すと、now関数だけでなく、その前にログが印刷されます.
実は,@logをnow()関数の定義の前に置くと,実行に相当する.
now = log(now)
logはdecoratorで、1つの関数を返して、返したこの関数の名前はwrapperで、元のnow()関数はまだ存在して、この時now変数はこの返し関数wrapperを指しました.now()を呼び出すと、新しい関数wrapper関数が実行されます.wrapper関数のパラメータは(*args,**kw)なので、wrapper()関数は任意のパラメータを受け入れることができます!wrapper関数の内部では、まずログを印刷し、元の関数を呼び出します.
分割線------------------------------------
decorator自体がパラメータを入力する必要がある場合は、decoratorを返す高次関数を記述する必要があります.たとえば、logのテキスト、定義、使用方法、および結果をカスタマイズします.
>>> def log(text):
... def decorator(func):
... def wrapper(*args,**kw):
... print('%s %s():' %(text,func.__name__))
... return func(*args,**kw)
... return wrapper
... return decorator
...
>>> @log(' ')
... def now():
... print('2017-12-28')
...
>>> now()
now():
2017-12-28
前の例では2層のdefネストが含まれており、後の例では3層のdefネストが含まれている.実際には、3つのネストの効果は似ています.
now=log('実行')(now)
解析:まずlog('実行')を実行し、decorator関数を返し、戻り関数を呼び出します.パラメータはnow関数で、最終的な戻り値はwrapper関数です.
ただし、次の文を実行してテストします.
>>> now.__name__
'wrapper'
decorator装飾後の関数はname__属性はnowからwrapperに変わりました.これは、返されるその関数wrapper関数の名前がwrapperであるため、元の関数の__をname__プロパティはwrapper関数にコピーされます.そうしないと、他の関数署名に依存するコードの実行がエラーになります.
実際にはwrapperを書く必要はありません.name__ = func.__name__このようなコードはpythonにfunctoolsを内蔵しています.wrapsはそのためです.最も
最後のステップでは、完全なdecoratorの書き方は以下の通りです.
>>> import functools
>>> def log(func):
... @functools.wraps(func)
... def wrapper(*args,**kw):
... print(' %s()' %func.__name__)
... return func(*args,**kw)
... return wrapper
...
>>> @log
... def now():
... print(' ')
...
>>> now
>>> now()
now()
パラメータ付きdecoratorの場合:
>>> import functools
>>> def log(text):
... def decorator(func):
... @functools.wraps(func)
... def wrapper(*args,**kw):
... print('%s %s()' %(text,func.__name__))
... return func(*args,**kw)
... return wrapper
... return decorator
...
>>> @log('ABC')
... def now():
... print(' ')
...
>>> now()
ABC now()
例:任意の関数に作用し、その関数の実行時間を印刷するdecoratorを設計します.
>>> import time,functools
>>> def log(func):
... @functools.wraps(func)
... def wrapper(*args,**kw):
... t1 = time.time()
... r = func(*args,**kw)
... print('%s excute in %s ms'%(func.__name__,1000*(time.time()-t1)))
... return r
... return wrapper
...
>>> @log
... def fast(x,y):
... return x+y
...
>>> @log
... def slow(x,y,z):
... time.sleep(0.1234)
... return x*y*z
...
>>> @log
... def fast(x,y):
... time.sleep(0.0012)
... return x+y
...
>>> fast(3,5)
fast excute in 2.0973682403564453 ms
8
>>> slow(4,5,6)
slow excute in 124.2520809173584 ms
120