【问题标题】:Python: building an LRU cachePython:构建 LRU 缓存
【发布时间】:2011-05-25 13:00:00
【问题描述】:

我有大约6,00,000 entries in MongoDB,格式如下:

feature:category:count

在哪里

  • feature 可以是任何词,
  • 类别是正面的还是负面的,并且
  • count 表示某个特征在该类别的文档中出现了多少次。

我想缓存前1000个元组,让我们这样说,以免每次都查询数据库。

如何在 Python 中构建 LRU 缓存?或者有任何已知的解决方案吗?

【问题讨论】:

    标签: python mongodb lru


    【解决方案1】:

    Python3.3 中的LRU cache 有O(1) 的插入、删除和搜索。

    该设计使用条目的循环双向链接列表(按从旧到新排列)和哈希表来定位各个链接。缓存命中使用哈希表查找相关链接并将其移动到列表的头部。缓存未命中删除最旧的链接并在链表的头部创建一个新链接。

    这是 33 行非常基本的 Python 的简化(但速度很快)版本(仅使用简单的字典和列表操作)。它在 Python2.0 及更高版本(或 PyPy 或 Jython 或 Python3.x)上运行:

    class LRU_Cache:
    
        def __init__(self, original_function, maxsize=1024):
            # Link structure: [PREV, NEXT, KEY, VALUE]
            self.root = [None, None, None, None]
            self.root[0] = self.root[1] = self.root
            self.original_function = original_function
            self.maxsize = maxsize
            self.mapping = {}
    
        def __call__(self, *key):
            mapping = self.mapping
            root = self.root
            link = mapping.get(key)
            if link is not None:
                link_prev, link_next, link_key, value = link
                link_prev[1] = link_next
                link_next[0] = link_prev
                last = root[0]
                last[1] = root[0] = link
                link[0] = last
                link[1] = root
                return value
            value = self.original_function(*key)
            if len(mapping) >= self.maxsize:
                oldest = root[1]
                next_oldest = oldest[1]
                root[1] = next_oldest
                next_oldest[0] = root
                del mapping[oldest[2]]
            last = root[0]
            last[1] = root[0] = mapping[key] = [last, root, key, value]
            return value
    
    
    if __name__ == '__main__':
        p = LRU_Cache(ord, maxsize=3)
        for c in 'abcdecaeaa':
            print(c, p(c))
    

    从 Python 3.1 开始,OrderedDict 使得实现 LRU 缓存变得更加简单:

    from collections import OrderedDict
    
    class LRU_Cache:
    
        def __init__(self, original_function, maxsize=1024):
            self.original_function = original_function
            self.maxsize = maxsize
            self.mapping = OrderedDict()
    
        def __call__(self, *key):
            mapping = self.mapping
            try:
                value = mapping[key]
                mapping.move_to_end(key)
            except KeyError:
                value = self.original_function(*key)
                if len(mapping) >= self.maxsize:
                    mapping.popitem(False)
                mapping[key] = value
            return value
    

    【讨论】:

      【解决方案2】:

      除了 Python 3.2 中包含的版本之外,Python Cookbook 中还有 LRU 缓存配方,包括 Python 核心开发人员 Raymond Hettinger 的these

      【讨论】:

        【解决方案3】:

        Python 3.2 functools 包括一个 LRU cache。你可以很容易地从repo 中挑选它,检查你是否必须调整它以使用 Python 2(不应该太难——也许使用itertools 而不是某些内置函数——询问你是否需要帮助)并完成。不过,您需要将查询包装到可调用对象中,并确保它依赖于(可散列的)函数参数。

        【讨论】:

        • 这看起来很有趣,但是我如何从 repo 中得到它???我不知道该怎么做\
        • @learner:最简单的方法是从我链接的文件中复制粘贴它。
        • 我试过了,但是当我尝试导入 functools 时它会抛出错误 >>> import functools Traceback(最近一次调用最后一次):文件“”,第 1 行,在 文件“functools .py",第 151 行非本地命中,未命中 ^ SyntaxError: invalid syntax Error in sys.excepthook:
        • @learner:啊,当然。 nonlocal 是一个支持闭包的新 python 3 关键字。它可以用一个肮脏但有效且简洁的hack来模拟。我想我解决了这个问题,但出现了一个新问题:它使用 collections.OrderedDict,它只包含在 2.7+ 中。此外,代码似乎被破坏了o.O(指的是不存在的OrderedDict.move_to_end)......
        • OrderedDict 实现可从pypi.python.org/pypi/ordereddict 获得。 cache.move_to_end(key) 很容易实现为 cache[key] = cache.pop(key)。肮脏的技巧是将hitsmisses初始化为hits_misses = [0, 0],并将hits替换为hits_misses[0],将misses替换为hits_misses[1]
        【解决方案4】:

        还有 lru_cache 的 python 3.3 版本的反向移植,例如在 python 2.7 上运行的this。如果您对两层缓存感兴趣(如果没有缓存在实例中,它将检查共享缓存)我已经基于 lru_cache 的反向端口创建了lru2cache

        【讨论】:

          猜你喜欢
          • 2012-08-15
          • 2013-08-02
          • 1970-01-01
          • 2014-06-22
          • 2011-01-31
          • 1970-01-01
          • 1970-01-01
          • 2011-03-02
          • 1970-01-01
          相关资源
          最近更新 更多