在某项目中,程序运行效率较差,为分析程序内哪些函数执行时间开销较大,我们实现了一个带timeout参数的函数装饰器。装饰功能如下:
@warn_timeout(1.5)def func(a,b): ...
要求:
统计被装饰函数单次调用运行时间;
时间大于参数timeout的,将此次函数调用记录到log日志中;
运行时可修稿timeout的值。
解决方案:
为包裹函数增加一个函数,用来修改闭包中使用的*变量。在Python3中,使用nonlocal
关键词访问嵌套作用域中的变量引用。
- 对于
nonlocal
关键字:
nonlocal
表示将变量声明为外层变量(外层函数的局部变量,而且不能是全局变量)。
global
表示将变量声明为全局变量。两个关键词都用于允许在一个局部作用域中使用外层的变量。
- 方案示例:
import time, logging, randomdef 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]