私は自分を適度な能力を持つPythonコーダーと考えるのが好きですが、私が決して理解できない言語の1つの側面はデコレータです。
私はそれらが(表面的に)知っており、チュートリアル、例、Stack Overflowに関する質問を読んでおり、構文を理解し、自分で書くことができ、時々@classmethodと@staticmethodを使用しますが、自分のPythonコードの問題を解決するデコレーター。 「うーん...これはデコレータの仕事のように見える!」と思う問題に遭遇することはありません。
それで、皆さんがあなた自身のプログラムでデコレーターを使用した例のいくつかを提供してくれるかもしれないと思っています。そして、できれば「A-ha!」があるでしょう。瞬間とgetそれら。
主にタイミングの目的でデコレータを使用しています
def time_dec(func):
def wrapper(*arg):
t = time.clock()
res = func(*arg)
print func.func_name, time.clock()-t
return res
return wrapper
@time_dec
def myFunction(n):
...
それらを同期に使用しました。
import functools
def synchronized(lock):
""" Synchronization decorator """
def wrap(f):
@functools.wraps(f)
def newFunction(*args, **kw):
lock.acquire()
try:
return f(*args, **kw)
finally:
lock.release()
return newFunction
return wrap
コメントで指摘したように、Python 2.5以降では、 with
ステートメントを threading.Lock
(または multiprocessing.Lock
バージョン2.6以降)オブジェクトは、デコレータの実装を次のように単純化します。
import functools
def synchronized(lock):
""" Synchronization decorator """
def wrap(f):
@functools.wraps(f)
def newFunction(*args, **kw):
with lock:
return f(*args, **kw)
return newFunction
return wrap
とにかく、次のように使用します。
import threading
lock = threading.Lock()
@synchronized(lock)
def do_something():
# etc
@synchronzied(lock)
def do_something_else():
# etc
基本的には、関数呼び出しの両側にlock.acquire()
/lock.release()
を置くだけです。
RMIを介してPythonメソッドに渡される型チェックパラメーターにデコレーターを使用します。したがって、同じパラメーターのカウントを繰り返す代わりに、例外を発生させるmumbo-jumboを何度も繰り返します
def myMethod(ID, name):
if not (myIsType(ID, 'uint') and myIsType(name, 'utf8string')):
raise BlaBlaException() ...
私はただ宣言します
@accepts(uint, utf8string)
def myMethod(ID, name):
...
そしてaccepts()がすべての仕事をしてくれます。
デコレータは、追加機能で透過的に「ラップ」したいものすべてに使用されます。
Djangoは、 「ビュー関数でのログインが必要」機能 のラッピング、および フィルター関数の登録 にこれらを使用します。
名前付きログをクラスに追加する にはクラスデコレータを使用できます。
既存のクラスまたは関数の動作に「タック」できる十分に汎用的な機能は、装飾のための公平なゲームです。
また、 PEP 318-関数とメソッドのデコレータ が指す Python-Devニュースグループに関するユースケースの議論 があります。
Twistedライブラリーは、ジェネレーターと組み合わされたデコレーターを使用して、非同期関数が同期的であるという錯覚を与えます。例えば:
@inlineCallbacks
def asyncf():
doStuff()
yield someAsynchronousCall()
doStuff()
yield someAsynchronousCall()
doStuff()
これを使用すると、多数の小さなコールバック関数に分割されるコードを1つのブロックとして非常に自然に記述できるため、理解と保守がはるかに容易になります。
ノーズテストでは、いくつかのパラメーターセットを持つ単体テスト関数またはメソッドを提供するデコレーターを作成できます。
@parameters(
(2, 4, 6),
(5, 6, 11),
)
def test_add(a, b, expected):
assert a + b == expected
明らかな用途の1つは、もちろんロギングです。
import functools
def log(logger, level='info'):
def log_decorator(fn):
@functools.wraps(fn)
def wrapper(*a, **kwa):
getattr(logger, level)(fn.__name__)
return fn(*a, **kwa)
return wrapper
return log_decorator
# later that day ...
@log(logging.getLogger('main'), level='warning')
def potentially_dangerous_function(times):
for _ in xrange(times): rockets.get_rocket(NUCLEAR=True).fire()
主にデバッグ(引数と結果を出力する関数のラッパー)と検証(引数が正しい型であるか、Webアプリケーションの場合、ユーザーが特定の方法)。
関数をスレッドセーフにするために、次のデコレータを使用しています。コードが読みやすくなります。 John Fouhyによって提案されたものとほとんど同じですが、違いは1つの関数で機能することと、ロックオブジェクトを明示的に作成する必要がないことです。
def threadsafe_function(fn):
"""decorator making sure that the decorated function is thread safe"""
lock = threading.Lock()
def new(*args, **kwargs):
lock.acquire()
try:
r = fn(*args, **kwargs)
except Exception as e:
raise e
finally:
lock.release()
return r
return new
class X:
var = 0
@threadsafe_function
def inc_var(self):
X.var += 1
return X.var
デコレータは、関数のプロパティを定義するか、それを変更する定型句として使用されます。完全に異なる関数を返すことは可能ですが、直感に反します。ここの他の応答を見ると、最も一般的な用途の1つは、ロギング、プロファイリング、セキュリティチェックなど、他のプロセスの範囲を制限することです。
CherryPyはオブジェクトディスパッチを使用して、URLとオブジェクト、そして最終的にはメソッドを照合します。これらのメソッドのデコレーターは、CherryPyがこれらのメソッドを使用することが許可されているか許可されているかを示します。たとえば、 チュートリアル から適応:
class HelloWorld:
...
def secret(self):
return "You shouldn't be here."
@cherrypy.expose
def index(self):
return "Hello world!"
cherrypy.quickstart(HelloWorld())
私は最近、ソーシャルネットワーキングWebアプリケーションの作業中にそれらを使用しました。コミュニティ/グループの場合、新しいディスカッションを作成し、その特定のグループのメンバーである必要があるメッセージに返信するために、メンバーシップの承認を与えることになっています。そこで、デコレータ@membership_required
を作成し、必要な場所に配置しました。
デコレータを使用して、関数メソッド変数を簡単に作成できます。
def static_var(varname, value):
'''
Decorator to create a static variable for the specified function
@param varname: static variable name
@param value: initial value for the variable
'''
def decorate(func):
setattr(func, varname, value)
return func
return decorate
@static_var("count", 0)
def mainCallCount():
mainCallCount.count += 1
このデコレータを使用してパラメータを修正します
def fill_it(arg):
if isinstance(arg, int):
return "wan" + str(arg)
else:
try:
# number present as string
if str(int(arg)) == arg:
return "wan" + arg
else:
# This should never happened
raise Exception("I dont know this " + arg)
print "What arg?"
except ValueError, e:
return arg
def fill_wanname(func):
def wrapper(arg):
filled = fill_it(arg)
return func(filled)
return wrapper
@fill_wanname
def get_iface_of(wanname):
global __iface_config__
return __iface_config__[wanname]['iface']
これは、いくつかの関数をリファクタリングして引数「wanN」を渡す必要があるときに書かれましたが、古いコードでは、Nまたは「N」のみを渡しました