在Python中智能缓存昂贵的对象

时间:2012-01-03 15:14:48

标签: python caching memory-management

我按顺序有一个图像目录。通常我的代码将使用来自图像的连续子集的数据(例如图像5-10),并且用于访问这些数据的天真选项是:

  1. 使用一种方法创建一个包装器对象,该方法在需要时加载图像并读取我的数据(例如像素值)。这几乎没有内存开销,但速度很慢,因为每次都需要加载每个图像。

  2. 将所有图像存储在内存中。这将很快,但显然我们可以存储的图像数量有限。

  3. 我想找到:

    • 我可以通过某种方法定义如何读取与索引或路径对应的图像,然后允许我访问,比如magic_image_collection[index],而不必担心它是否会返回对象记忆或重读。理想情况下,这将保留适当的图像或最近访问过的n最近访问的图像。

2 个答案:

答案 0 :(得分:5)

如果密钥丢失,您可以扩展默认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'

一个演变是只存储dict中的N个最后一个元素,并清除最旧的条目。您可以通过保留用于订购的密钥列表来实现它。

答案 1 :(得分:2)

Weakrefs不是你想要的 - weakrefs是一种引用一个项目的方法,它允许垃圾收集器收集(即销毁)指示对象,如果只存在弱引用的话。换句话说,如果你只创建弱存储并将其存储到某个对象,很可能会很快收集垃圾,你也不会从中受益。

我选择上面的选项#1。在现代操作系统上,操作系统维护最近访问的文件(或其部分)的内存缓存,这意味着您将不得不承担从磁盘加载文件一次的成本,但之后,后续访问该文件将与您的应用程序内存一样快(或几乎如此)。 FS缓存通常是LRU样式的缓存,因此频繁访问的项目将倾向于保留在内存中,而不经常访问的项目将倾向于被逐出(并且如果需要,随后将从磁盘加载)。在大多数情况下,依靠操作系统实现这种逻辑就足够了,而不是编写自己的逻辑(特别是因为你不必编写和维护代码来执行它!)