Python中昂贵对象的智能缓存

Python中昂贵对象的智能缓存,python,caching,memory-management,Python,Caching,Memory Management,我有一个有序的图像目录。通常,我的代码将使用图像序列子集(例如图像5-10)中的数据,访问这些图像的简单选项包括: 使用在需要时加载图像并读取我的数据(例如像素值)的方法创建包装器对象。这几乎没有内存开销,但速度很慢,因为每次都需要加载每个图像 将所有图像存储在内存中。这会很快,但很明显,我们可以存储的图像数量是有限的 我想找到: 通过某种方法,我可以定义如何读取对应于索引或路径的图像,然后允许我访问,比如说magic\u image\u collection[index],而不必担心它是否将

我有一个有序的图像目录。通常,我的代码将使用图像序列子集(例如图像5-10)中的数据,访问这些图像的简单选项包括:

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

  • 将所有图像存储在内存中。这会很快,但很明显,我们可以存储的图像数量是有限的

  • 我想找到:

    • 通过某种方法,我可以定义如何读取对应于索引或路径的图像,然后允许我访问,比如说
      magic\u image\u collection[index]
      ,而不必担心它是否将返回内存中的对象或重新读取它。这将理想地将适当的图像或最近访问的图像保存在内存中

    Weakrefs不是您想要的——Weakrefs是一种引用项的方法,它允许垃圾收集器收集(即销毁)引用对象(如果只有Weakrefs存在)。换句话说,如果您只创建并存储某个对象的weakrefs,那么它很可能会很快被垃圾收集,并且您不会从中受益


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

    Weakrefs不是您想要的——Weakrefs是引用允许垃圾收集器收集(即销毁)的项的一种方式引用对象如果只存在对其不利的因素。换句话说,如果您只创建并存储某个对象的weakrefs,那么它很可能会很快被垃圾收集,并且您不会从中受益


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

    您可以扩展默认dict,并使用
    \u missing\u
    方法在缺少键时调用加载函数:

    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
    
    以及输出(仅当密钥尚不存在时才会加载)


    一种改进是只存储dict中最后的N个元素,并清除最早的条目。您可以通过保留一个键列表进行排序来实现它。

    您可以扩展默认dict,并在缺少键时使用
    \uuuuuu missing\uuuu
    方法调用加载函数:

    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
    
    以及输出(仅当密钥尚不存在时才会加载)


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

    感谢weakrefs的澄清。我将尝试选项1和@tito的想法。感谢weakrefs的澄清。我将同时尝试选项1和@tito的想法。摆脱
    \uuu getitem\uuuuuuu
    并将load重命名为
    \uuu missing\uuuuuu
    ,您应该会没事的。在
    \uu missing\uuuuu
    中,您所要做的就是为键返回适当的值,或者引发异常。调用
    dict
    代码将负责更新dict(您的类从中继承)。要添加对“last n elements”的支持,请添加一个键列表作为成员,并将该键添加到
    \uuuu missing\uuuu
    中列表的末尾。当列表超过n时,从列表和self中弹出最旧的(0)键。谢谢Paul,不知道
    \uuuu缺少\uuu
    ,非常好@tito-如果我正确阅读了文档,您甚至不必在
    \uuuuu missing\uuuuu
    中分配给self[key],只需返回它(或者在密钥无效时引发异常)。文档代码会处理剩下的。保罗就在那里。我会把这个标记为正确的@介意我整理一下,并按照Paul的建议添加“last n elements”功能吗?去掉
    \uuuu getitem\uuuuuuu
    并将load重命名为
    \uuu missing\uuuuuuuuuu
    ,您应该会没事的。在
    \uuuu missing\uuuuuu
    中,您所要做的就是为键返回适当的值,或者引发异常。调用
    dict
    代码将负责更新dict(您的类从中继承)。要添加对“last n elements”的支持,请添加一个键列表作为成员,并将该键添加到
    \uuuu missing\uuuu
    中列表的末尾。当列表超过n时,从列表和self中弹出最旧的(0)键。谢谢Paul,不知道
    \uuuu缺少\uuu
    ,非常好@tito-如果我正确阅读了文档,您甚至不必在
    \uuuuu missing\uuuuuu
    中分配给self[key],只需返回它(或者在密钥无效时引发异常)