昨年、Habréでデコレータに関する
2つの パートで非常に詳細な記事がすでにありました。 この新しい記事の目的は、前の記事よりもさらに洗練された例を理解するための時間を持たせるために、興味を引く意味のある例にすぐに
取り掛かり 、すぐに従事することです。
対象読者は、高階関数やクロージャーに既に慣れているプログラマー(C#など)ですが、関数内の注釈は「メタ情報」であり、それ自体がリフレクションでのみ現れるという事実に慣れています。 このようなプログラマの目をすぐに引くPythonの機能は、関数を宣言する前にデコレータが存在することで、この関数の
動作を変更できることです。

どのように機能しますか? 
トリッキーなことは何もありません。デコレータは、関数を修飾するための引数を取り、「修正された」ものを返す単なる関数です。
def timed(fn): def decorated(*x): start = time() result = fn(*x) print "Executing %s took %d ms" % (fn.__name__, (time()-start)*1000) return result return decorated @timed def cpuload(): load = psutil.cpu_percent() print "cpuload() returns %d" % load return load print "cpuload.__name__==" + cpuload.__name__ print "CPU load is %d%%" % cpuload() 
( 完全なソース ) cpuload .__ name __ ==デコレーション
 cpuload()は16を返します
 cpuloadの実行には105ミリ秒かかりました
 CPU負荷は16%です
宣言
@timed def cpuload(): ...は
def cpuload(): ...; cpuload=timed(cpuload)展開され
def cpuload(): ...; cpuload=timed(cpuload) def cpuload(): ...; cpuload=timed(cpuload)であるため、結果として、グローバル名
cpuload 、 
timed内の
decorated関数に関連付けられ、 
fn変数を介して元の
cpuload関数に閉じられます。 その結果、 
cpuload.__name__==decorated値が関数を取り、関数を返す関数である式は、デコレーターとして使用できます。 したがって、「パラメーター付きのデコレーター」(実際には、デコレーターファクトリー)を作成することができます。
 def repeat(times): """   times ,     """ def decorator(fn): def decorated2(*x): total = 0 for i in range(times): total += fn(*x) return total / times return decorated2 return decorator @repeat(5) def cpuload(): """   cpuload    """ print "cpuload.__name__==" + cpuload.__name__ print "CPU load is %d%%" % cpuload() 
( 完全なソース ) cpuload .__ name __ == decorated2
 cpuload()は7を返します
 cpuload()は16を返します
 cpuload()は0を返します
 cpuload()は0を返します
 cpuload()は33を返します
 CPU負荷は11%です
repeat(5)式の値は、 
times=5閉じられる
decorator関数です。 この値はデコレーターとして使用されます。 実際には
def cpuload(): ...; cpuload=repeat(5)(cpuload) def cpuload(): ...; cpuload=repeat(5)(cpuload)1つの関数で複数のデコレータを組み合わせると、自然な順序で右から左に適用されます。 前の2つの例を
@timed @repeat(5) def cpuload():に結合すると、-出力が得られます
 cpuload .__ name __ ==デコレーション
 cpuload()は28を返します
 cpuload()は16を返します
 cpuload()は0を返します
 cpuload()は0を返します
 cpuload()は0を返します
 decor2の実行には503ミリ秒かかりました
 CPU負荷は9%です
そして、デコレータの順序を変更すると- 
@repeat(5) @timed def cpuload(): -取得します
 cpuload .__ name __ == decorated2
 cpuload()は16を返します
 cpuloadの実行には100ミリ秒かかりました
 cpuload()は14を返します
 cpuloadの実行には109ミリ秒かかりました
 cpuload()は0を返します
 cpuloadの実行には101ミリ秒かかりました
 cpuload()は0を返します
 cpuloadの実行には100ミリ秒かかりました
 cpuload()は0を返します
 cpuloadの実行には99ミリ秒かかりました
 CPU負荷は6%です
最初のケースでは、広告は
cpuload=timed(repeat(5)(cpuload))に展開され、2番目のケースでは
cpuload=repeat(5)(timed(cpuload)) 。 印刷された関数名に注意してください。どちらの場合でも、呼び出しのチェーンを追跡できます。
パラメトリックデコレーションの制限ケースは、 
デコレータをパラメータとして取るデコレータです 。
 def toggle(decorator): """  ""  ""  """ def new_decorator(fn): decorated = decorator(fn) def new_decorated(*x): if decorator.enabled: return decorated(*x) else: return fn(*x) return new_decorated decorator.enabled = True return new_decorator @toggle(timed) def cpuload(): """   cpuload    """ print "cpuload.__name__==" + cpuload.__name__ print "CPU load is %d%%" % cpuload() timed.enabled = False print "CPU load is %d%%" % cpuload() 
( 完全なソース ) cpuload .__ name __ == new_decorated
 cpuload()は28を返します
 cpuloadの実行には101ミリ秒かかりました
 CPU負荷は28%です
 cpuload()は0を返します
 CPU負荷は0%です
デコレータの接続/切断を制御する値は、装飾された関数の
enabled属性に保存されます。Pythonでは、任意の関数に任意の属性を「固定」できます。
結果の
toggle関数は、 
デコレータのデコレータとしても使用でき
toggle 。
 @toggle def timed(fn): """   timed    """ @toggle def repeat(times): """   repeat    """ @timed @repeat(5) def cpuload(): """   cpuload    """ print "cpuload.__name__==" + cpuload.__name__ print "CPU load is %d%%" % cpuload() timed.enabled = False print "CPU load is %d%%" % cpuload() 
( 完全なソース ) cpuload .__ name __ == new_decorated
 cpuload()は28を返します
 cpuload()は0を返します
 cpuload()は0を返します
 cpuload()は0を返します
 cpuload()は0を返します
 decor2の実行には501ミリ秒かかりました
 CPU負荷は5%です
 cpuload()は0を返します
 cpuload()は16を返します
 cpuload()は14を返します
 cpuload()は16を返します
 cpuload()は0を返します
 decor2の実行には500ミリ秒かかりました
 CPU負荷は9%です
ええと...いや、うまくいきませんでした! しかし、なぜですか?
なぜ
cpuload 2回
cpuload呼び出されても、 
timedデコレータがオフにならないのですか?
timedのグローバル名は、装飾されたデコレータに関連付けられていることを思い出してください。 
new_decorated関数を使用。 これは、 
timed.enabled = Falseの行が実際に
new_decorated関数の属性を変更することを意味します-両方のデコレーターの共通の「ラッパー」です。 
if decorator.enabled:代わりに
new_decorated内で
new_decorated if decorator.enabled:か
if new_decorator.enabled:を確認すること
if new_decorator.enabled:が、 
timed.enabled = Falseの行は両方のデコレーターを一度に無効にします。
このバグを修正しましょう。「内部」デコレータで
enabled属性を使用するために、以前と同様に、 
new_decorated関数にいくつかのメソッドを
new_decoratedます。
 def toggle(decorator): """  ""  ""  """ def new_decorator(fn): decorated = decorator(fn) def new_decorated(*x):  
( 完全なソース )希望する結果が達成されます-切断された
timedますが、 
repeat続けます:
 cpuload .__ name __ == new_decorated
 cpuload()は14を返します
 cpuload()は16を返します
 cpuload()は0を返します
 cpuload()は0を返します
 cpuload()は0を返します
 decor2の実行には503ミリ秒かかりました 

 CPU負荷は6%です
 cpuload()は0を返します
 cpuload()は0を返します
 cpuload()は7を返します
 cpuload()は0を返します
 cpuload()は0を返します
 CPU負荷は1%です
これはPythonの最も魅力的な機能の1つです。関数だけでなく、任意の関数メソッドにも属性を追加できます。 関数は関数に置かれ、関数を駆動します。