[Python实践高级] 深入理解cache并根据需要进行实践

最近项目上遇到,缓存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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值