您可以通过将@warvariuc's answer 与问题Decorator that prints function call details (parameters names and effective values)? 合并到您自己的装饰器中来做这样的事情:
import inspect
import time
def warn_slow(func):
'''Decorator that warns when a call to the function takes too long.'''
TIME_LIMIT = 2
def wrapper(*args, **kwargs):
func_args = inspect.signature(func).bind(*args, **kwargs).arguments
func_args_str = ", ".join("{}={!r}".format(*item) for item in func_args.items())
start_time = time.time()
result = func(*args, **kwargs)
time_elapsed = time.time() - start_time
if time_elapsed > TIME_LIMIT:
print(f"execution time of {func.__module__}.{func.__qualname__}"
f"({func_args_str}) took more than {TIME_LIMIT} seconds")
return result
return wrapper
@warn_slow
def test_func_a(a, b, c=None):
time.sleep(1)
@warn_slow
def test_func_b(a, b, c=None):
time.sleep(2.5)
test_func_a(1, 2, c=13)
test_func_b(3, 4, c=42)
产生的输出:
execution time of __main__.test_func_b(a=3, b=4, c=42) took more than 2 seconds
概括
在编程时,通常最好避免将常量字面值“硬编码”到代码中。在这种情况下,装饰器可以通过允许覆盖 2 秒时间限制来变得更加灵活。一种方法需要让它接受一个可选参数,指定限制应该是什么。为了实现这一点,我使用了来自@Nicole's answer 的通用“装饰器装饰器”来解决如何实现让它们这样做的问题。我选择它的部分原因是它需要对上面显示的装饰器进行最少的修改(而且它本身非常通用)。
结果如下:
import inspect
import time
def optional_arg_decorator(fn): # From https://stackoverflow.com/a/20966822/355230
def wrapped_decorator(*args):
if len(args) == 1 and callable(args[0]):
return fn(args[0])
else:
def real_decorator(decoratee):
return fn(decoratee, *args)
return real_decorator
return wrapped_decorator
@optional_arg_decorator
def warn_slow(func, time_limit=2):
''' Decorator that warns when a call to the function takes too long.
Accepts optional argument to override default time limit.
'''
def wrapper(*args, **kwargs):
func_args = inspect.signature(func).bind(*args, **kwargs).arguments
func_args_str = ", ".join("{}={!r}".format(*item) for item in func_args.items())
start_time = time.time()
result = func(*args, **kwargs)
time_elapsed = time.time() - start_time
if time_elapsed > time_limit:
print(f"execution time of {func.__module__}.{func.__qualname__}"
f"({func_args_str}) took more than {time_limit} seconds")
return result
return wrapper
@warn_slow
def test_func_a(a, b, c=None):
time.sleep(1)
@warn_slow
def test_func_b(a, b, c=None):
time.sleep(2.5)
@warn_slow(3) # Override default time limit.
def test_func_c(a, b, c=None):
time.sleep(3.1)
test_func_a(1, 2, c=10)
test_func_b(3, 4, c=2)
test_func_c(5, 6, c=42)
输出:
execution time of __main__.test_func_b(a=3, b=4, c=2) took more than 2 seconds
execution time of __main__.test_func_c(a=5, b=6, c=42) took more than 3 seconds