Python 实现一个计时功能的装饰器
下面的装饰器clock会打印函数的运行时间
定义了一个内部函数clocked,它接受任意定位参数以及关键字参数。
这行代码可用,是因为clocked的闭包中包含了自由变量func。
返回内部的函数,取代被装饰的函数。
functools.wraps是标准库中拿来即用的装饰器之一,它的作用是协助构建行为良好的装饰器。如果不加
functools.wraps(func)
, 会遮盖被装饰函数的__name__
和doc
属性,functools.lrucache()是非常实用的装饰器,它实现了备忘(memoization)功能。这是一项优化技术,它把耗时的函数的结果保存起来,避免传入相同的参数时重复计算。LRU三个字母是“Least Recently Used”的缩写,表明缓存不会无限制增长,一段时间不用的缓存条目会被扔掉。这样就会显著提高程序的运行效率。lrucache可以使用两个可选的参数来配置。它的方法签名是
lru_cache(maxsize=128, typed=False)
。maxsize参数指定存储多少个调用的结果。缓存满了之后,旧的结果会被扔掉,腾出空间。为了得到最佳性能,maxsize应该设为2的幂。typed参数如果设为True,把不同参数类型得到的结果分开保存,即把通常认为相等的浮点数和整数参数(如 1 和 1.0)区分开。顺便说一下,因为lrucache使用字典存储结果,而且键根据调用时传入的定位参数和关键字参数创建,所以被lrucache装饰的函数,它的所有参数都必须是可散列的。
实际上该处的工作原理如下:
等价于
fib会作为func参数传给clock。然后,clock函数会返回clocked函数,Python解释器在背后会把 clocked赋值给fib。实际上如果我们没有添加@functools.wraps(func)
,我们在ipython中导入describe进一步观察:
所以,现在fib保存的是 clocked 函数的引用。自此之后,每次调用 fib(n),执行的都是 clocked(n)。clocked 大致做了下面几件事。
(1) 记录初始时间 t0。
(2) 调用原来的 fib 函数,保存结果。
(3) 计算经过的时间。
(4) 打印出来格式化收集的数据
(5) 返回第 2 步保存的结果。
这是装饰器的典型行为:把被装饰的函数替换成新函数,二者接受相同的参数,而且(通常)返回被装饰的函数本该返回的值,同时还会做些额外操作。
版权声明: 本文为 InfoQ 作者【王坤祥】的原创文章。
原文链接:【http://xie.infoq.cn/article/3eb700110257afe2862b89b83】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论