Elm*_*der 5 python caching garbage-collection weak-references
根据weakref模块的官方Python文档,"弱引用的主要用途是实现保存大对象的缓存或映射,......".因此,我使用WeakValueDictionary为长时间运行的函数实现缓存机制.然而,事实证明,缓存中的值从未停留在那里,直到实际再次使用它们,但几乎每次都需要重新计算.由于访问存储在WeakValueDictionary中的值之间没有强引用,因此GC消除了它们(即使内存完全没有问题).
现在,我如何使用弱参考资料来实现缓存?如果我明确地在某处保留强引用以防止GC删除我的弱引用,那么首先使用WeakValueDictionary是没有意义的.GC可能应该有一些选项告诉它:删除所有没有引用的内容以及只有在内存不足时(或超出某个阈值时)弱引用的所有内容.有类似的东西吗?或者这种缓存有更好的策略吗?
我将尝试通过如何使用该weakref
模块实现缓存的示例来回答您的询问。我们将缓存的弱引用保存在 a 中weakref.WeakValueDictionary
,强引用保存在 a 中,collections.deque
因为它有一个maxlen
属性来控制它持有多少对象。以函数闭包方式实现:
import weakref, collections
def createLRUCache(factory, maxlen=64):
weak = weakref.WeakValueDictionary()
strong = collections.deque(maxlen=maxlen)
notFound = object()
def fetch(key):
value = weak.get(key, notFound)
if value is notFound:
weak[key] = value = factory(key)
strong.append(value)
return value
return fetch
Run Code Online (Sandbox Code Playgroud)
该deque
对象将只保留最后一个maxlen
条目,一旦达到容量,只需删除对旧条目的引用。当旧条目被删除并被 python 收集垃圾时,WeakValueDictionary
将从地图中删除这些键。因此,这两个对象的组合有助于我们仅maxlen
在 LRU 缓存中保留条目。
class Silly(object):
def __init__(self, v):
self.v = v
def fib(i):
if i > 1:
return Silly(_fibCache(i-1).v + _fibCache(i-2).v)
elif i: return Silly(1)
else: return Silly(0)
_fibCache = createLRUCache(fib)
Run Code Online (Sandbox Code Playgroud)