在Python中智能缓存昂贵对象
我有一个按顺序排列的图片文件夹。通常我的代码会使用一部分连续的图片数据(比如第5到第10张),而获取这些图片的简单方法有:
创建一个包装对象,里面有一个方法可以在需要的时候加载图片并读取我的数据(比如一个像素值)。这种方法占用的内存很少,但速度会比较慢,因为每次都需要重新加载每一张图片。
把所有的图片都存放在内存里。这种方法速度快,但显然我们能存放的图片数量是有限的。
我想找到一种方法:
- 可以定义如何根据索引或路径读取对应的图片,然后让我可以像这样访问,比如
magic_image_collection[index]
,而不需要担心它是从内存中返回对象还是重新读取。这种方法最好能把合适的图片或者最近访问的n
张图片保存在内存里。
2 个回答
2
弱引用(weakrefs)并不是你想要的东西。弱引用是一种引用方式,它允许垃圾回收器在只有弱引用存在时,回收(也就是销毁)被引用的对象。换句话说,如果你只创建并存储某个对象的弱引用,这个对象很可能会被快速回收,你就无法使用它了。
我建议你选择上面提到的选项 #1。在现代操作系统中,系统会维护一个内存缓存,用来存储最近访问过的文件(或文件的一部分)。这意味着你只需承担一次从硬盘加载文件的成本,但之后再次访问这个文件时,速度会和在你应用程序内存中一样快(或者几乎一样快)。文件系统缓存通常是基于最近最少使用(LRU)策略的,所以经常访问的文件会留在内存中,而不常访问的文件则可能会被移除(如果需要的话,之后会从硬盘重新加载)。在大多数情况下,依赖操作系统来处理这种逻辑就足够了,而不需要自己编写和维护相关代码!
6
你可以扩展默认的字典(dict),并使用 __missing__
方法来调用一个加载函数,当你查找的键不存在时:
class ImageDict(dict):
def __missing__(self, key):
self[key] = img = self.load(key)
return img
def load(self, key):
# create a queue if not exist (could be moved to __init__)
if not hasattr(self, '_queue'):
self._queue = []
# pop the oldest entry in the list and the dict
if len(self._queue) >= 100:
self.pop(self._queue.pop(0))
# append this key as a newest entry in the queue
self._queue.append(key)
# implement image loading here and return the image instance
print 'loading', key
return 'Image for %s' % key
输出结果(只有在键不存在时才会进行加载。)
>>> d = ImageDict()
>>> d[3]
loading 3
'Image for 3'
>>> d[3]
'Image for 3'
>>> d['bleh']
loading bleh
'Image for bleh'
>>> d['bleh']
'Image for bleh'
一种改进的方法是只在字典中存储最近的 N 个元素,并删除最旧的条目。你可以通过保持一个键的列表来实现这个排序。