在某项目中,程序运行效率较差,为分析程序内哪些函数执行时间开销较大,我们实现了一个带timeout参数的函数装饰器。装饰功能如下:
@warn_timeout(1.5)
def func(a,b):
...
要求:
-
统计被装饰函数单次调用运行时间;
-
时间大于参数timeout的,将此次函数调用记录到log日志中;
-
运行时可修稿timeout的值。
解决方案:
为包裹函数增加一个函数,用来修改闭包中使用的自由变量。在Python3中,使用nonlocal
关键词访问嵌套作用域中的变量引用。
- 对于
nonlocal
关键字:
nonlocal
表示将变量声明为外层变量(外层函数的局部变量,而且不能是全局变量)。
global
表示将变量声明为全局变量。两个关键词都用于允许在一个局部作用域中使用外层的变量。
- 方案示例:
import time, logging, random
def warn_timeout(timeout):
def decorator(func):
def wrap(*args, **kwargs):
t0 = time.time()
res = func(*args, **kwargs)
used = time.time() - t0
if used > timeout:
logging.warning('%s: %s > %s' % (func.__name__, used, timeout))
return res
def set_timeout(new_timeout):
nonlocal timeout
timeout = new_timeout
wrap.set_timeout = set_timeout
return wrap
return decorator
@warn_timeout(1)
def f(i):
print('in f [%s]' % i)
while random.randint(0, 1):
time.sleep(0.6)
for i in range(10):
f(i)
f.set_timeout(1.5)
for i in range(10):
f(i)
结果:
in f [0]
in f [1]
in f [2]
in f [3]
in f [4]
WARNING:root:f: 2.40358567237854 > 1
in f [5]
in f [6]
WARNING:root:f: 1.2016241550445557 > 1
in f [7]
WARNING:root:f: 1.8050942420959473 > 1
in f [8]
in f [9]
WARNING:root:f: 1.8009305000305176 > 1
in f [0]
in f [1]
in f [2]
in f [3]
in f [4]
in f [5]
in f [6]
WARNING:root:f: 1.802403211593628 > 1.5
in f [7]
in f [8]
in f [9]