cacheutils -缓存和缓存¶
cacheutils 包含基本缓存类型的一致实现。目前有两种可供选择:
两个缓存都是 dict 子类型,设计为尽可能可互换,以便于实验。使用缓存增强性能的一个关键实践是确保缓存策略有效。如果缓存不断丢失,只会增加更多的开销和代码复杂性。标准统计数据为:
hit_count-查询键在缓存中的次数
miss_count-关键字不存在和/或被缓存提取的次数
soft_miss_count-没有密钥,但调用者提供了默认密钥的次数,如dict.get()和dict.setdefault()。软未命中是未命中的子集,因此该数字始终小于或等于miss_count。
另外, cacheutils 提供 ThresholdCounter ,这是一个类似缓存的绑定计数器,可用于在线统计信息收集。
了解更多有关 caching algorithms on Wikipedia 。
最近最少插入(LRI)¶
这个 LRI 是一种更简单的缓存,它实现了一种非常简单的先进先出(FIFO)方法来清除缓存。如果用例需要简单、开销非常低的缓存,例如开销较高的本地操作(例如,字符串操作),那么LRI可能是正确的选择。
- class boltons.cacheutils.LRI(max_size=128, values=None, on_miss=None)[源代码]¶
这个
LRI实现基本的 Least Recently Inserted 从战略到缓存。人们也可以认为这是一种SizeLimitedDefaultDict。on_miss 是接受缺失密钥的可调用对象(与
collections.defaultdict的“DEFAULT_FACTORY”,它不接受任何参数。)还要注意,与LRIvt.的.LRI配备了统计信息跟踪工具。>>> cap_cache = LRI(max_size=2) >>> cap_cache['a'], cap_cache['b'] = 'A', 'B' >>> from pprint import pprint as pp >>> pp(dict(cap_cache)) {'a': 'A', 'b': 'B'} >>> [cap_cache['b'] for i in range(3)][0] 'B' >>> cap_cache['c'] = 'C' >>> print(cap_cache.get('a')) None >>> cap_cache.hit_count, cap_cache.miss_count, cap_cache.soft_miss_count (3, 1, 1)
最近最少使用(LRU)¶
这个 LRU 是更高级的缓存,但它仍然非常简单。当它达到容量时,新的插入项将替换最近最少使用的项。对于各种应用程序,此策略使LRU成为比LRI更有效的缓存,但也需要对其所有API执行更多操作,尤其是读取。不像 LRI ,LRU内置了螺纹安全。
- class boltons.cacheutils.LRU(max_size=128, values=None, on_miss=None)[源代码]¶
这个
LRU是dict的子类型实现 Least-Recently Used 缓存策略。- 参数:
max_size (int) -- 要缓存的最大项目数。默认为
128。values (iterable) -- 缓存的初始值。默认为
None。on_miss (callable) -- 接受单个参数(缓存中不存在的键)并返回要缓存的值的可调用对象。
>>> cap_cache = LRU(max_size=2) >>> cap_cache['a'], cap_cache['b'] = 'A', 'B' >>> from pprint import pprint as pp >>> pp(dict(cap_cache)) {'a': 'A', 'b': 'B'} >>> [cap_cache['b'] for i in range(3)][0] 'B' >>> cap_cache['c'] = 'C' >>> print(cap_cache.get('a')) None
该缓存还配备了统计信息收集工具。
hit_count,miss_count,及soft_miss_count都是可用于自省缓存性能的整数成员。(“软”未命中是指未引起KeyError,例如,LRU.get()或on_miss用于缓存默认设置。>>> cap_cache.hit_count, cap_cache.miss_count, cap_cache.soft_miss_count (3, 1, 1)
除了大小限制的高速缓存行为和统计之外,
LRU与其父类(内置的Python)类似dict。
自动函数缓存¶
继续高速缓存可调整性和实验的主题, cacheutils 还提供了一种可插入的方法来缓存函数返回值: cached() 函数装饰符和 cachedmethod() 方法修饰器。
- boltons.cacheutils.cached(cache, scoped=True, typed=False, key=None)[源代码]¶
使用您选择的缓存对象缓存任何函数。请注意,包装的函数应该只接受 hashable 争论。
- 参数:
>>> my_cache = LRU() >>> @cached(my_cache) ... def cached_lower(x): ... return x.lower() ... >>> cached_lower("CaChInG's FuN AgAiN!") "caching's fun again!" >>> len(my_cache) 1
- boltons.cacheutils.cachedmethod(cache, scoped=True, typed=False, key=None)[源代码]¶
类似于
cached(),cachedmethod用于根据方法的参数缓存方法,使用dict-喜欢 cache 对象。- 参数:
cache (str/Mapping/callable) -- 可以是实例上属性的名称,可以是任何映射/:类:类似于`DICTION`的对象,也可以是返回映射的Callable。
scoped (bool) -- 方法本身及其绑定到的对象是否为缓存键的一部分。
True默认情况下,不同的方法不会读取彼此的缓存结果。False对于某些共享缓存使用情形非常有用。更高级的行为可以通过 key 争论。typed (bool) -- 是否将参数类型作为缓存检查的因素。默认
False,设置为True导致的缓存键3和3.0被认为不平等。key (callable) -- 具有匹配签名的可调用对象
make_cache_key()它返回要用作缓存中的键的Hasable值的元组。
>>> class Lowerer(object): ... def __init__(self): ... self.cache = LRI() ... ... @cachedmethod('cache') ... def lower(self, text): ... return text.lower() ... >>> lowerer = Lowerer() >>> lowerer.lower('WOW WHO COULD GUESS CACHING COULD BE SO NEAT') 'wow who could guess caching could be so neat' >>> len(lowerer.cache) 1
类似的功能可以在Python3.4中找到 functools.lru_cache() 修饰器,但函数工具方法不支持相同的缓存策略修改,也不支持跨多个函数共享缓存对象。
阈值有界计数¶
- class boltons.cacheutils.ThresholdCounter(threshold=0.001)[源代码]¶
A bounded 从关键字到计数的类似字典的映射。ThresholdCounter在以下时间间隔(1/ threshold )添加,维护其计数至少表示 threshold 总数据的比率。换句话说,如果ThresholdCounter中不存在特定的键,则其计数小于 threshold 在全部数据中。
>>> tc = ThresholdCounter(threshold=0.1) >>> tc.add(1) >>> tc.items() [(1, 1)] >>> tc.update([2] * 10) >>> tc.get(1) 0 >>> tc.add(5) >>> 5 in tc True >>> len(list(tc.elements())) 11
正如您在上面看到的,该API保持与
collections.Counter。最显著的功能遗漏是,不能直接设置、不计算或删除已计算的项,因为这会扰乱计算。当您需要动态键控数据的尽力而为的长期计数时,请使用ThresholdCounter。如果没有像这样的受限数据结构,动态键通常表示内存泄漏,并可能影响应用程序的可靠性。ThresholdCounter的项替换策略是完全确定的,可以被认为是 Amortized Least Relevant 。它将存储的密钥的绝对上限为 (2/threshold) ,但实际上 (1/threshold) 对于均匀随机的数据流,预计要高出一个或两个数量级,而对于真实世界的数据,则要高出一个或两个数量级。
该算法是Manku&Motwani在《数据流上的近似频率计数》中描述的有损计数算法的实现。向库尔特·罗斯致敬,感谢他的发现和初步实施。