摘自流畅的Python 第七章 函数装饰器和闭包
使用functools.lru_cache 做备忘functools.lru_cache 是非常使用的装饰器,它实现了备忘(memoization)功能。这是一项优化技术,它把耗时的函数的结果保存起来,避免传入相同的参数是重复计算
clockdeco.py
import time def clock(func): def clocked(*args): t0 = time.time() result = func(*args) elapsed = time.time() - t0 name = func.__name__ arg_str = ', '.join(repr(arg) for arg in args) print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result)) return result return clocked
我们使用斐波那契数列作为例子 观察
fibo_demo.py
from clockdeco import clock import time @clock def fibonacci(n): if n < 2: return n return fibonacci(n-2) + fibonacci(n-1) if __name__=='__main__': start = time.time() print(fibonacci(30)) print(time.time() - start)
部分输出如下
... [0.92167187s] fibonacci(24) -> 46368 [1.41511607s] fibonacci(25) -> 75025 [2.26029801s] fibonacci(26) -> 121393 [3.61707902s] fibonacci(27) -> 196418 [5.80630493s] fibonacci(28) -> 317811 [9.39297986s] fibonacci(29) -> 514229 [15.82301521s] fibonacci(30) -> 832040 832040 15.82302474975586
浪费时间的地方很明显:fibonacci(1) 调用了8次,fibonacci(5)调用了5次…
现在我们在代码中添加lru_cache
fibo_demo_lru.py
import functools import time from clockdeco import clock @functools.lru_cache() # 注意,必须像常规函数那样调用 lru_cache。这一行中有一对括 号:@functools.lru_cache()。这么做的原因是,lru_cache 可以 接受配置参数,稍后说明。 @clock # 这里叠放了装饰器:@lru_cache() 应用到 @clock 返回的函数上。这样一来,执行时间减半了,而且 n 的每个值只调用一次函数: def fibonacci(n): if n < 2: return n return fibonacci(n - 2) + fibonacci(n - 1) if __name__ == '__main__': start = time.time() print(fibonacci(30)) print(time.time() - start)
部分输出如下
[0.00000119s] fibonacci(25) -> 75025 [0.00016594s] fibonacci(26) -> 121393 [0.00000000s] fibonacci(27) -> 196418 [0.00020003s] fibonacci(28) -> 317811 [0.00000072s] fibonacci(29) -> 514229 [0.00020981s] fibonacci(30) -> 832040 832040 0.00021791458129882812
由此可以看出使用lru_cache 优化的斐波那契 在计算的耗时方面极大程度的优化了
特别要注意,lru_cache可以使用两个可选的参数来配置。它的签名是
functools.lru_cache(maxsize=128, typed=False)
maxsize参数指定存储多少个调用的结果。缓存慢了之后,旧的结果会被扔掉,腾出空间。为了得到最佳性能,maxsize应该设为2的幂。
type参数如果设为True,把不同参数类型得到的结果分开保存,即把通常人为相等的浮点数和整数参数(如1和1.0)区分开。顺便说一下,因为lru_cache使用字典存储结果,而且键根据调用是传入的定位参数和关键字参数创建,所以被lru_cache装饰的函数,它的所有参数都必须是可散列的
假设我们在开发一个调试Web应用的工具,我们想生成HTML,显示不同类型的Python对象。我们可能会编写这样的函数
import html def htmlize(obj): # repr() 函数将对象转化为供解释器读取的形式 content = html.escape(repr(obj)) return '{}'.format(content) print(htmlize({1,2,3}))
这个函数使用与任何Python类型,但是现在我们想做个拓展,让它使用特别的方式显示某些类型
str: 把内部的换行符替换为’
n’; 不使用
,而是使用int: 以十进制和十六进制显示数字list: 输出一个HTML列表,根据各个元素的类型进行格式化
{1, 2, 3}因为Python不支持重载方法或函数,所以我们不能使用不同的签名定义htmlize变体,也无法使用不同的方式处理不同的数据类型
generic.py
from functools import singledispatch from collections import abc import numbers import html # @singledispatch 标记处理 object 类型的基函数。 @singledispatch def htmlize(obj): content = html.escape(repr(obj)) return '{}'.format(content) # 各个专门函数使用 @«base_function».register(«type») 装饰。 @htmlize.register(str) def _(text): content = html.escape(text).replace('n', '
n') return '{0}
'.format(content) @htmlize.register(numbers.Integral) def _(n): return '{0} (0x{0:x})'.format(n) # 可以叠放多个 register 装饰器,让同一个函数支持不同类型。 @htmlize.register(tuple) @htmlize.register(abc.MutableSequence) def _(seq): inner = 'n'.join(htmlize(item) for item in seq) return 'n' + inner + 'n' print(htmlize({1,2,3})) print('-'*40) print(htmlize(abs)) print('-'*40) print(htmlize('Heimlich & Co.n- a game')) print('-'*40) print(htmlize(42)) print('-'*40) print(htmlize(['alpha', 66, {3, 2, 1}]))
输出如下
{1, 2, 3}----------------------------------------<built-in function abs>----------------------------------------Heimlich & Co.
----------------------------------------
- a game42 (0x2a)----------------------------------------
alpha
66 (0x42){1, 2, 3}欢迎分享,转载请注明来源:内存溢出
评论列表(0条)