Recentemente, escrevi um decorador que faz isso:
@log_runtime('calling foo')
def foo():
do_stuff()
Então, profundamente em alguma outra função, eu queria fazer isso:
with log_runtime('do other stuff'):
do_other_stuff()
Isso levou a uma importante realização: conceitualmente, a maioria dos decoradores reais são gerenciadores de contexto .
Eu amo gerentes de contexto, então comecei a escrever todos os decoradores como gerentes de contexto-decorador híbridos hipercarregados:
from functools import wraps
class ContextDecorator(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __enter__(self):
# Note: Returning self means that in "with ... as x", x will be self
return self
def __exit__(self, typ, val, traceback):
pass
def __call__(self, f):
@wraps(f)
def wrapper(*args, **kw):
with self:
return f(*args, **kw)
return wrapper
Agora, posso escrever log_runtime
assim:
class log_runtime(ContextDecorator):
def __enter__(self):
self.start_time = time.time()
return self
def __exit__(self, typ, val, traceback):
# Note: typ, val and traceback will only be not None
# If an exception occured
logger.info("{}: {}".format(self.label, time.time() - self.start))
e use-o assim:
@log_runtime(label="foo")
def foo():
do_stuff()
e assim:
with log_runtime(label="bar"):
do_bar_stuff()