参考文档:
- https://zhuanlan.zhihu.com/p/27643991
- https://docs.python.org/3.7/library/functools.html#functools.lru_cache
- https://segmentfault.com/a/1190000009398663
functools.cmp_to_key(func)
将旧式比较函数转换为关键字函数。与接受字关键函数(如sort()、min()、max()、heapq. nbiggest()、heapq.nsmallest()、itertools.groupby())的工具一起使用。该函数主要用于从Python 2转换过来的程序的转换工具,Python 2支持使用比较函数。
比较函数是任何可调用的函数,它接受两个参数,进行比较,然后返回负数(小于)、零(相等)或正数(大于)。键函数是一个可调用函数,它接受一个参数并返回另一个值作为排序键。
放上一波源码:
################################################################################
### cmp_to_key() function converter
################################################################################
def cmp_to_key(mycmp):
"""Convert a cmp= function into a key= function"""
class K(object):
__slots__ = ['obj']
def __init__(self, obj):
self.obj = obj
def __lt__(self, other):
return mycmp(self.obj, other.obj) < 0
def __gt__(self, other):
return mycmp(self.obj, other.obj) > 0
def __eq__(self, other):
return mycmp(self.obj, other.obj) == 0
def __le__(self, other):
return mycmp(self.obj, other.obj) <= 0
def __ge__(self, other):
return mycmp(self.obj, other.obj) >= 0
__hash__ = None
return K
写一个demo,看一下运行流程:
import functools
class MyObject:
def __init__(self, val):
self.val = val
def __str__(self):
return 'MyObject({})'.format(self.val)
def compare_obj(a, b):
"""Old-style comparison function.
"""
print('comparing {} and {}'.format(a, b))
if a.val < b.val:
return -1
elif a.val > b.val:
return 1
return 0
# 排序的key
get_key = functools.cmp_to_key(compare_obj)
def get_key_wrapper(o):
"Wrapper function for get_key to allow for print statements."
new_key = get_key(o)
print('key_wrapper({}) -> {!r}'.format(o, new_key))
return new_key
objs = [MyObject(x) for x in range(5, 0, -1)]
for o in sorted(objs, key=get_key_wrapper):
print(o)
通常情况下,cmp_to_key()
将直接使用,但在本例中引入了一个额外的包装函数,以在调用关键函数时输出更多信息。
输出如下:
key_wrapper(MyObject(5)) -> <functools.KeyWrapper object at 0x10692e510>
key_wrapper(MyObject(4)) -> <functools.KeyWrapper object at 0x10692e4f0>
key_wrapper(MyObject(3)) -> <functools.KeyWrapper object at 0x10692e4d0>
key_wrapper(MyObject(2)) -> <functools.KeyWrapper object at 0x10692e470>
key_wrapper(MyObject(1)) -> <functools.KeyWrapper object at 0x10692e490>
comparing MyObject(4) and MyObject(5)
comparing MyObject(3) and MyObject(4)
comparing MyObject(2) and MyObject(3)
comparing MyObject(1) and MyObject(2)
MyObject(1)
MyObject(2)
MyObject(3)
MyObject(4)
MyObject(5)
@functools.lru_cache(maxsize=128, typed=False)
这个装饰器实现了备忘的功能,是一项优化技术,把耗时的函数的结果保存起来,避免传入相同的参数时重复计算。lru 是(least recently used)的缩写,即最近最少使用原则。表明缓存不会无限制增长,一段时间不用的缓存条目会被扔掉。
这个装饰器支持传入参数,还能有这种操作的?maxsize 是保存最近多少个调用的结果,最好设置为 2 的倍数,默认为 128。如果设置为 None 的话就相当于是 maxsize 为正无穷了。还有一个参数是 type,如果 type 设置为 true,即把不同参数类型得到的结果分开保存,如 f(3) 和 f(3.0) 会被区分开。
由于字典用于缓存结果,因此函数的位置和关键字参数必须是hashable的。
如果maxsize设置为None,则禁用LRU特性,缓存可以无限制增长。当maxsize为2次方时,LRU特性表现最佳。
如果将类型设置为true,则将分别缓存不同类型的函数参数。例如,f(3)和f(3.0)将被视为具有不同结果的不同调用。
为了帮助度量缓存的有效性并调优maxsize参数,封装的函数使用cache_info()函数进行检测,该函数返回一个命名元组,显示hits(命中), misses(未命中)、maxsize和currsize。在多线程环境中,得失是近似的。
decorator还提供了一个cache_clear()函数,用于清除或使缓存失效。
原始的底层函数可以通过wrapped属性访问。这对于内省、绕过缓存或使用不同的缓存重新包装函数非常有用。
写了个函数追踪结果
def track(func):
@functools.wraps(func)
def inner(*args):
result = func(*args)
print("{} --> ({}) --> {} ".format(func.__name__, args[0], result))
return result
return inner
递归函数适合使用这个装饰器,那就拿经典的斐波那契数列来测试吧
不使用缓存
@track
def fib(n):
if n < 2:
return n
return fib(n - 2) + fib(n - 1)
使用缓存
@functools.lru_cache()
@track
def fib_with_cache(n):
if n < 2:
return n
return fib_with_cache(n - 2) + fib_with_cache(n - 1)
测试代码
fib(10)
fib