最近项目上遇到,缓存dict类型加载资源。正常的lru_cache()函数不支持dict缓存。只好改造。
思路
1、字典的形式保存缓存数据,同时增加增加过期时间,如{‘key’:{‘expire’: 1524363282, ‘data’: 2}},但这样的话何时回收呢,如果单独起个程序扫描过期的数据清除key,貌似又过于复杂了,这里采用弱引用。
2、WeakValueDictionary的特性:如果value值没有强引用了,那么对应的记录就会被回收
3、所以还需要定义strongRef来强引用,并放入collections.deque来保持强引用,deque队列可定义大小,那么超过阀值的老元素就会被弹出,也就消除了强引用,也就会被马上回收
讲解
对对象的弱引用不能保证对象存活:当对像的引用只剩弱引用时, garbage collection 可以销毁引用并将其内存重用于其他内容。但是,在实际销毁对象之前,即使没有强引用,弱引用也一直能返回该对象。
弱引用的主要用途是实现保存大对象的高速缓存或映射,但又并希望大对象仅仅因为它出现在高速缓存或映射中而保持存活。
例如,如果您有许多大型二进制图像对象,则可能希望将名称与每个对象关联起来。如果您使用Python字典将名称映射到图像,或将图像映射到名称,则图像对象将保持活动状态,因为它们在字典中显示为值或键。 weakref 模块提供的 WeakKeyDictionary 和 WeakValueDictionary 类可以替代Python字典,使用弱引用来构造映射,这些映射不会仅仅因为它们出现在映射对象中而使对象保持存活。例如,如果一个图像对象是 WeakValueDictionary 中的值,那么当对该图像对象的剩余引用是弱映射对象所持有的弱引用时,垃圾回收可以回收该对象并将其在弱映射对象中相应的条目删除。
WeakKeyDictionary 和 WeakValueDictionary 在它们的实现中使用弱引用,在弱引用上设置回调函数,当键或值被垃圾回收回收时通知弱字典。 WeakSet 实现了 set 接口,但像 WeakKeyDictionary 一样,只持有其元素的弱引用。`
finalize 提供了注册一个对象被垃圾收集时要调用的清理函数的方式。这比在原始弱引用上设置回调函数更简单,因为模块会自动确保对象被回收前终结器一直保持存活。
这些弱容器类型之一或者 finalize 就是大多数程序所需要的 - 通常不需要直接创建自己的弱引用。weakref 模块暴露了低级机制,以便于高级用途。
import weakref, collections
class LocalCache():
notFound = object()
# list dict等不支持弱引用,但其子类支持,故这里包装了下
class Dict(dict):
def __del__(self):
pass
def __init__(self, maxlen=10):
self.weak = weakref.WeakValueDictionary()
self.strong = collections.deque(maxlen=maxlen)
@staticmethod
def nowTime():
return int(time.time())
def get(self, key):
value = self.weak.get(key, self.notFound)
if value is not self.notFound:
expire = value[r'expire']
if self.nowTime() > expire:
return self.notFound
else:
return value
else:
return self.notFound
def set(self, key, value):
# strongRef作为强引用避免被回收
self.weak[key] = strongRef = LocalCache.Dict(value)
# 放入定大队列,弹出元素马上被回收
self.strong.append(strongRef)
# 装饰器
from functools import wraps
def funcCache(expire=0):
caches = LocalCache()
def _wrappend(func):
@wraps(func)
def __wrapped(*args, **kwargs):
key = str(func) + str(args) + str(kwargs)
result = caches.get(key)
if result is LocalCache.notFound:
result = func(*args, **kwargs)
caches.set(key, {r'result': result, r'expire': expire + caches.nowTime()})
result = caches.get(key)
return result
return __wrapped
return _wrappend
# 测试函数
import time
@funcCache(expire=300)
def test_cache(v):
# 模拟任务处理时常3秒
time.sleep(3)
print('gkate 3s')
return v
print(test_cache(1))
print(test_cache(2))
print(test_cache(1))
print(test_cache(2))
参考:
https://x.hacking8.com/post-273.html
http://cn.voidcc.com/question/p-qibzhfsq-baq.html