デコレータは本質的に Python 関数であり、コードを変更することなく他の関数が関数を追加できるようになります。デコレータの戻り値も関数オブジェクトです。.
は、ログ挿入、パフォーマンス テスト、トランザクション処理、キャッシュ、権限の検証など、横断的な要件を持つシナリオでよく使用されます。デコレータはこのような問題を解決する優れた設計であり、関数自体とは関係のない類似コードを大量に抽出して再利用し続けることができます。
まず簡単な例を見てみましょう:
def now(): print('2017_7_29')
新しい要件があります。関数の実行ログを記録したいので、ログ コードをコードに追加します。 :
def now(): print('2017_7_29') logging.warn("running")
同様の要件が複数あるとします。どうすればよいでしょうか? now 関数に別のレコードを書き込みますか?これにより、同様のコードが大量に作成されます。コードの繰り返し記述を減らすために、関数を再定義できます。具体的にはログを処理し、ログの処理後に実際のビジネス コードを実行します。
def use_logging(func): logging.warn("%s is running" % func.__name__) func() def now(): print('2017_7_29') use_logging(now)
実装では、は論理的には難しくありませんが、この場合、関数をパラメータとして毎回log関数に渡す必要があります。また、この方法では元のコードの論理構造が破壊されてしまうため、以前はビジネスロジックを実行する際にnow()を実行していましたが、use_logging(now)に変更する必要があります。
では、もっと良い方法はあるでしょうか?もちろん、それはあります。答えはデコレータです。
まず、関数はオブジェクトでもあり、関数オブジェクトは変数に代入できるため、変数を介して関数を呼び出すこともできることを理解する必要があります。例:
(=
単純なデコレータ
本質的に、デコレータは関数を返す高階関数です。したがって、ログを出力できるデコレータを定義する必要があります。これは次のように定義できます。
def log(func): def wrapper(*args,**kw): print('call %s():'%func.__name__) return func(*args,**kw) return wrapper # 由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在, # 只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。 # wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。 # 在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
上記の log
はデコレータであるため、関数をパラメータとして受け取り、関数を返します。Now 実行:
now = log(now) now()
输出结果: call now(): 2017_7_28
Functionlog はデコレータです。実際のビジネス メソッドを実行する func を関数内でラップします。log によって修飾されているように見えます。この例では、関数が入ることをアスペクト(Aspect)と呼び、このプログラミング方法をアスペクト指向プログラミング(Aspect-Oriented Programming)と呼びます。
使用糖鎖構文:
@logdef now(): print('2017_7_28')
@シンボルはデコレーターの糖鎖構文です。別の代入操作を避けるために関数を定義するときに使用されます
Inこのようにして、now = log(now) という文を省略し、now() を直接呼び出して、目的の結果を得ることができます。他に同様の関数がある場合は、関数を繰り返し変更したり、新しいパッケージを追加したりすることなく、引き続きデコレータを呼び出して関数を修飾することができます。これにより、プログラムの再利用性が向上し、可読性が向上します。
デコレータが Python で使用するのに非常に便利である理由は、Python 関数を通常のオブジェクトと同様に他の関数にパラメータとして渡したり、他の変数に代入したり、戻り値として使用したりできるためです。 . 別の関数内で定義できます。
パラメータ付きのデコレータ:
デコレータ自体がパラメータを渡す必要がある場合は、次のような高い値を書き込む必要があります。デコレータを返します。 Order 関数の作成は少し複雑です。たとえば、ログのテキストをカスタマイズするには:
def log(text): def decorator(func): def wrapper(*args,**kw): print('%s %s()'%(text,func.__name__)) return func(*args,**kw) return wrapper return decorator
この 3 層のネストされたデコレータの使用法は次のとおりです:
@log(() now()
は
<span style="color: #000000;">now = log('goal')(now)<br># 首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数<br>now()</span>
と同等です。関数もオブジェクトであると述べていますが、 __name__
のような属性がありますが、デコレータで修飾された関数を見ると、その __name__
は元の ' から変更されています。 now'
から 'wrapper'
:
print(now.__name__)# wrapper
返された wrapper()
関数名は 'wrapper'
であるため、元の関数 ##__name__ の # を変更する必要があり、他の属性は
wrapper() 関数にコピーされます。そうしないと、関数シグネチャに依存する一部のコードが正しく実行されません。
wrapper.__name__ = func.__name__ のようなコードを記述する必要はありません。Python の組み込み
functools.wraps がこれを行うため、完全なデコレータとなります。
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
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
クラスデコレータ:
クラスデコレータをもう一度見てみましょう 関数デコレータに比べて、クラスデコレータは柔軟性が高く、高含有量、重合、カプセル化などの利点。クラス デコレータを使用すると、クラス内の __call__ メソッドに依存することもできます。@ フォームを使用してデコレータを関数に付加すると、このメソッドが呼び出されます。import time class Foo(object): def __init__(self, func): self._func = func def __call__(self): print ('class decorator runing') self._func() print ('class decorator ending') @Foo def now(): print (time.strftime('%Y-%m-%d',time.localtime(time.time()))) now()
同時に、オブジェクト指向 (OOP) 設計モードでは、デコレーターはデコレーション モードと呼ばれます。 OOP のデコレーション モードは、継承と組み合わせを通じて実装する必要があります。Python は、OOP のデコレーターのサポートに加えて、構文レベルから直接デコレーターもサポートします。 Python のデコレータは関数またはクラスとして実装できます。
さらに関連する知識については、Python ビデオ チュートリアル 列
を参照してください。以上がPython デコレータの詳細の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。