関数を条件付きでデコレータすることは可能ですか?たとえば、関数foo()
をタイマー関数(timeit
)で装飾したいのですが、doing_performance_analysisはTrue
だけです(以下の擬似コードを参照)。
if doing_performance_analysis:
@timeit
def foo():
"""
do something, timeit function will return the time it takes
"""
time.sleep(2)
else:
def foo():
time.sleep(2)
デコレータは、置換、オプションで同じ関数、ラッパー、またはまったく異なるものを返す単純な呼び出し可能オブジェクトです。そのため、条件付きデコレータを作成できます。
_def conditional_decorator(dec, condition):
def decorator(func):
if not condition:
# Return the function unchanged, not decorated.
return func
return dec(func)
return decorator
_
これで、次のように使用できます。
_@conditional_decorator(timeit, doing_performance_analysis)
def foo():
time.sleep(2)
_
デコレータはクラスにすることもできます。
_class conditional_decorator(object):
def __init__(self, dec, condition):
self.decorator = dec
self.condition = condition
def __call__(self, func):
if not self.condition:
# Return the function unchanged, not decorated.
return func
return self.decorator(func)
_
ここで、___call__
_メソッドは、最初の例で返されたdecorator()
ネストされた関数と同じ役割を果たし、ここで閉じられたdec
およびcondition
パラメーターは次のとおりです。デコレータが適用されるまで、インスタンスに引数として格納されます。
デコレータは、単に別の関数に適用される関数です。手動で適用できます。
def foo():
# whatever
time.sleep(2)
if doing_performance_analysis:
foo = timeit(foo)
どうですか:
def foo():
...
if doing_performance_analysis:
foo = timeit(foo)
これをブールフラグと別のデコレータを使用するデコレータにラップし、フラグがTrue
に設定されている場合にのみ後者を適用できると思います。
def cond_decorator(flag, dec):
def decorate(fn):
return dec(fn) if flag else fn
return decorate
@cond_decorator(doing_performance_analysis, timeit)
def foo():
...
use_decorator = False
class myDecorator(object):
def __init__(self, f):
self.f = f
def __call__(self):
print "Decorated running..."
print "Entering", self.f.__name__
self.f()
print "Exited", self.f.__name__
def null(a):
return a
if use_decorator == False :
myDecorator = null
@myDecorator
def CoreFunction():
print "Core Function running"
CoreFunction()
Blckknghtの答えは、関数を呼び出すたびにチェックを実行したい場合に最適ですが、一度読み取ることができ、変更されない設定がある場合は、装飾された関数が呼び出されるたびに設定をチェックしたくない場合があります。動作中の高性能デーモンのいくつかでは、pythonファイルが最初にロードされたときに設定ファイルを一度チェックし、それをラップするかどうかを決定するデコレータを作成しました。
これがサンプルです
def timed(f):
def wrapper(*args, **kwargs):
start = datetime.datetime.utcnow()
return_value = f(*args, **kwargs)
end = datetime.datetime.utcnow()
duration = end - start
log_function_call(module=f.__module__, function=f.__name__, start=__start__, end=__end__, duration=duration.total_seconds())
if config.get('RUN_TIMED_FUNCTIONS'):
return wrapper
return f
Log_function_callがデータベースやログファイルなどへの呼び出しをログに記録し、config.get( 'RUN_TIMED_FUNCTIONS')がグローバル構成をチェックし、@ timedデコレータを関数に追加すると、ロード時に1回チェックされ、タイミングが合っているかどうかが確認されます。このサーバー、環境など。そうでない場合でも、本番環境やパフォーマンスが重要なその他の環境での関数の実行は変更されません。
これが私のために働いたものです:
def timeit(method):
def timed(*args, **kw):
if 'usetimer' not in kw:
return method(*args, **kw)
Elif ('usetimer' in kw and kw.get('usetimer') is None):
return method(*args, **kw)
else:
import time
ts = time.time()
result = method(*args, **kw)
te = time.time()
if 'log_time' in kw:
name = kw.get('log_name', method.__name__.upper())
kw['log_time'][name] = int((te - ts) * 1000)
else:
print '%r took %2.2f ms' % \
(method.__name__, (te - ts) * 1000)
return result
return timed
def some_func(arg1, **kwargs):
#do something here
some_func(param1, **{'usetimer': args.usetimer})