5.7 KiB
5.7 KiB
ObjectCache 技术文档
# ObjectCache 类技术文档
## 概述
`ObjectCache` 是一个基于字典的内存对象缓存类,用于管理具有大小属性的对象集合。它继承自 Python 内置的 `dict` 类,并扩展了容量限制、自动淘汰和访问时间追踪功能。
该缓存适用于需要控制内存使用量、并能通过 `.get_size()` 方法获取对象大小的场景。当缓存总大小接近预设上限时,会触发基于最近最少使用(LRU)策略的半数淘汰机制。
---
## 特性
- 支持类似字典的键值存储操作。
- 自动跟踪每个对象的大小(需实现 `get_size()` 方法)。
- 设置最大缓存容量(以字节或其他单位计),防止内存溢出。
- 使用 LRU(最近最少使用)策略进行对象淘汰。
- 记录对象最后访问时间以支持淘汰逻辑。
---
## 类定义
```python
class ObjectCache(dict)
继承自 dict,因此支持所有标准字典操作(如 in, len(), 迭代等)。
初始化方法
__init__(self, maxsize=10000000, *args)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
maxsize |
int | 缓存允许的最大总大小,默认为 10,000,000 单位(例如字节)。 |
*args |
tuple | 传递给父类 dict 的初始化参数(可选)。 |
属性初始化:
| 属性 | 类型 | 说明 |
|---|---|---|
self.maxsize |
int | 最大缓存容量限制。 |
self.size |
int | 当前缓存中所有对象的总大小,初始为 0。 |
self._shadow |
dict | 私有字典,记录每个键对应的 [最后访问时间, 对象大小]。 |
⚠️ 注意:必须导入
time模块,否则在设置项时会抛出NameError。
核心方法
__setitem__(key, item)
将对象插入或更新到缓存中。
行为流程:
- 调用
item.get_size()获取对象大小。 - 若失败(无此方法或异常),则直接返回,不缓存该对象。
- 成功获取大小后,将其加到
self.size。 - 如果当前总大小超过
maxsize:- 从
self._shadow中提取所有(访问时间, key)元组。 - 按访问时间排序(越早访问的排在前面)。
- 删除前一半最久未访问的条目及其对应的数据。
- 从
- 调用父类
__setitem__存储新对象。 - 在
_shadow中记录其访问时间和大小。
注意事项:
- 存在 bug:
tmp[i][i]应为tmp[i][1](即 key),原代码会导致索引错误。 - 正确写法应为:
for i in xrange(len(tmp)//2): del self[tmp[i][1]] # tmp[i] 是 (time, key),所以取 [1]
__getitem__(key)
获取指定键对应的对象。
行为流程:
- 尝试调用父类
__getitem__获取对象。 - 若存在,则更新该键在
_shadow中的访问时间为当前时间(time.time())。 - 返回对象。
异常处理:
- 若键不存在,重新抛出
KeyError。
get(key, default=None)
安全获取指定键的对象,若不存在返回默认值。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
key |
any | 要查找的键。 |
default |
any | 键不存在时返回的默认值,默认为 None。 |
实现逻辑:
- 调用
self.has_key(key)判断是否存在。 - 存在则返回
self[key](触发__getitem__并更新时间戳)。 - 否则返回
default。
✅ 推荐使用方式:比直接捕获异常更安全。
__delitem__(key)
删除指定键的对象。
行为流程:
- 调用父类
__delitem__删除主缓存中的对象。 - 若成功,从
self.size中减去该对象的大小。 - 从
self._shadow中删除对应的元数据。
异常处理:
- 删除失败时重新抛出异常。
使用示例
import time
class MyData:
def __init__(self, data):
self.data = data
def get_size(self):
return len(self.data)
# 创建缓存实例,最大容量为 1MB
cache = ObjectCache(maxsize=1024*1024)
# 添加对象
obj = MyData("Hello World" * 100)
cache['key1'] = obj
# 获取对象
data = cache.get('key1')
print(data)
# 删除对象
del cache['key1']
已知问题与改进建议
❌ Bug 修复建议
在 __setitem__ 方法中:
for i in xrange(len(tmp)//2) :
del self[tmp[i][i]]
应改为:
for _, key in tmp[:len(tmp)//2]:
del self[key]
或者修正索引错误:
for i in range(len(tmp)//2):
del self[tmp[i][1]] # 取出 key
此外,Python 2 风格的 xrange 和 .iteritems() 建议升级为 Python 3 兼容语法。
⚙️ 性能优化建议
- 当前淘汰策略每次都要排序全部元素,复杂度为 O(n log n),效率较低。
- 可替换为优先队列或双向链表实现真正的 LRU 缓存,提升性能。
_shadow结构可以考虑合并进主字典,减少维护成本。
💡 扩展功能建议
- 提供
clear()方法重写以同步清理_shadow和size。 - 添加
__contains__、keys()等方法的行为说明。 - 增加线程安全锁(多线程环境下使用时)。
依赖要求
- Python 2.7 或兼容版本(当前代码为 Python 2 风格)
- 必须导入模块:
否则运行时报错。import time
总结
ObjectCache 是一个轻量级的对象缓存工具,适合对内存敏感的应用场景。尽管存在一些实现缺陷,但其设计思路清晰,易于理解和扩展。建议在生产环境中结合更成熟的缓存库(如 functools.lru_cache 或第三方库 cachetools)使用,或在此基础上修复 bug 后封装成稳定组件。