cacheutils -缓存和缓存

cacheutils 包含基本缓存类型的一致实现。目前有两种可供选择:

  • LRI -最近插入的时间最少

  • LRU -最近最少使用

两个缓存都是 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”,它不接受任何参数。)还要注意,与 LRI vt.的. 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)
clear() None.  Remove all items from D.[源代码]
copy() a shallow copy of D[源代码]
get(key, default=None)[源代码]

如果key在字典中,则返回key的值,否则返回Default。

pop(k[, d]) v, remove specified key and return the corresponding value.[源代码]

如果未找到密钥,则返回默认值(如果给定),否则引发KeyError

popitem()[源代码]

移除(键、值)对并将其作为二元组返回。

对按后进先出(LIFO)顺序返回。如果判定为空,则引发KeyError。

setdefault(key, default=None)[源代码]

如果Key不在词典中,则插入值为Default的Key。

如果key在字典中,则返回key的值,否则返回Default。

update([E, ]**F) None.  Update D from dict/iterable E and F.[源代码]

如果E存在并且具有.key()方法,则会这样做:for k in E:D [k] =E [k] 如果E存在并且缺少.key()方法,则会这样做:对于k,E:D中的v [k] =v在任何一种情况下,后跟:表示F:D中的k [k] =F [k]

最近最少使用(LRU)

这个 LRU 是更高级的缓存,但它仍然非常简单。当它达到容量时,新的插入项将替换最近最少使用的项。对于各种应用程序,此策略使LRU成为比LRI更有效的缓存,但也需要对其所有API执行更多操作,尤其是读取。不像 LRI ,LRU内置了螺纹安全。

class boltons.cacheutils.LRU(max_size=128, values=None, on_miss=None)[源代码]

这个 LRUdict 的子类型实现 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_countmiss_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 争论。

参数:
  • cache (Mapping) -- 任何 dict -适合用作缓存的类似对象。的实例 LRULRI 都是不错的选择,但平淡无奇 dict 在某些情况下也能奏效。此参数也可以是不接受任何参数并返回映射的可调用函数。

  • scoped (bool) -- 函数本身是否为缓存键的一部分。 True 默认情况下,不同的函数不会读取彼此的缓存项,但可以逐出彼此的结果。 False 对于某些共享缓存使用情形非常有用。更高级的行为可以通过 key 争论。

  • typed (bool) -- 是否将参数类型作为缓存检查的因素。默认 False ,设置为 True 导致的缓存键 33.0 被认为不平等。

>>> 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 导致的缓存键 33.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() 修饰器,但函数工具方法不支持相同的缓存策略修改,也不支持跨多个函数共享缓存对象。

boltons.cacheutils.cachedproperty(func)[源代码]

这个 cachedproperty 的用法与 property ,只是包装的方法只调用一次。这通常用于实现惰性属性。

访问属性后,该值存储在实例本身上,使用与cachedProperty相同的名称。这允许使用以下命令清除缓存 delattr() ,或通过操作对象的 __dict__

阈值有界计数

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在《数据流上的近似频率计数》中描述的有损计数算法的实现。向库尔特·罗斯致敬,感谢他的发现和初步实施。

add(key)[源代码]

将计数递增 key 按1,如果不存在则自动添加。

缓存压缩触发的频率为 1/threshold 加法。

elements()[源代码]

返回计数器跟踪的所有公共元素的迭代器。生成每个密钥的次数与它被看到的次数相同。

get(key, default=0)[源代码]

统计以下项目 key ,默认为0。

get_common_count()[源代码]

获取超过配置的数据阈值的密钥的计数和。

get_commonality()[源代码]

获取有效计数精度的浮点表示形式。数字越大,添加的关键点就越不均匀,ThresholdCounter的准确性和效率就越高。

如果需要更强的数据基数度量,请考虑使用超对数。

get_uncommon_count()[源代码]

获取由于关联计数小于配置的阈值而被剔除的键的计数总和。长尾也很重要。

most_common(n=None)[源代码]

拿到顶端 n 键和计数为元组。如果 n 被省略,则返回所有对。

update(iterable, **kwargs)[源代码]

与DICT.UPDATE()类似,但添加计数而不是替换计数,用于在一次调用中添加多个项。

源可以是要添加的键的可迭代,也可以是键到整数计数的映射。