如何限制字典的大小?

ant*_*ony 53 python caching dictionary lru

我想在python中使用dict,但是将键/值对的数量限制为X.换句话说,如果dict当前存储X键/值对并执行插入,我想要一个要删除的现有对.如果它是最近最少插入/访问密钥会很好,但这不是完全必要的.

如果这个存在于标准库中,请节省一些时间并指出它!

小智 42

Python 2.7和3.1有OrderedDict,并且有早期Pythons的纯Python实现.

from collections import OrderedDict

class LimitedSizeDict(OrderedDict):
    def __init__(self, *args, **kwds):
        self.size_limit = kwds.pop("size_limit", None)
        OrderedDict.__init__(self, *args, **kwds)
        self._check_size_limit()

    def __setitem__(self, key, value):
        OrderedDict.__setitem__(self, key, value)
        self._check_size_limit()

    def _check_size_limit(self):
        if self.size_limit is not None:
            while len(self) > self.size_limit:
                self.popitem(last=False)
Run Code Online (Sandbox Code Playgroud)

您还必须覆盖可以插入项的其他方法,例如update.主要用途OrderedDict是你可以控制容易弹出的东西,否则正常dict就可以了.

  • 准确地说,这不是一个真正的LRU实现.由于字典大小限制,这是FIFO删除.为了拥有完整的LRU实现,需要覆盖`__contains__`方法将最后一项"used"或查询移动到dict链表的顶部.[我明白了,这不是问题的主要目的] (3认同)
  • 我认为这个答案的评价太高了,因为它没有显示出如何实现可能也需要的任何其他方法,这不仅需要包括作者提到的添加/插入元素的方法,还需要包括删除/删除它们。 (2认同)

vaa*_*aab 17

cachetools将为您提供很好的Mapping Hashes实现(它可以在python 2和3上运行).

摘录文件:

出于此模块的目的,缓存是固定最大大小的可变映射.当高速缓存已满时,即通过添加另一个项目,高速缓存将超过其最大大小,高速缓存必须基于合适的高速缓存算法选择要丢弃的项目.


Ale*_*lli 10

这是一个简单的,没有LRU的Python 2.6+解决方案(在较旧的Pythons中你可以做类似的事情UserDict.DictMixin,但在2.6和更好的情况下不推荐,而且collections最好是ABCs ...):

import collections

class MyDict(collections.MutableMapping):
    def __init__(self, maxlen, *a, **k):
        self.maxlen = maxlen
        self.d = dict(*a, **k)
        while len(self) > maxlen:
            self.popitem()
    def __iter__(self):
        return iter(self.d)
    def __len__(self):
        return len(self.d)
    def __getitem__(self, k):
        return self.d[k]
    def __delitem__(self, k):
        del self.d[k]
    def __setitem__(self, k, v):
        if k not in self and len(self) == self.maxlen:
            self.popitem()
        self.d[k] = v

d = MyDict(5)
for i in range(10):
    d[i] = i
    print(sorted(d))
Run Code Online (Sandbox Code Playgroud)

正如其他提到的答案,你可能不希望子类dict - 显然委托到self.d遗憾的是样板,但它确实保证了所有其他方法都是正确提供的collections.MutableMapping.


Ray*_*ger 8

这是一个简单而有效的LRU缓存,使用简单的Python代码编写,可在任何python 1.5.2或更高版本上运行:

class LRU_Cache:

    def __init__(self, original_function, maxsize=1000):
        self.original_function = original_function
        self.maxsize = maxsize
        self.mapping = {}

        PREV, NEXT, KEY, VALUE = 0, 1, 2, 3         # link fields
        self.head = [None, None, None, None]        # oldest
        self.tail = [self.head, None, None, None]   # newest
        self.head[NEXT] = self.tail

    def __call__(self, *key):
        PREV, NEXT = 0, 1
        mapping, head, tail = self.mapping, self.head, self.tail

        link = mapping.get(key, head)
        if link is head:
            value = self.original_function(*key)
            if len(mapping) >= self.maxsize:
                old_prev, old_next, old_key, old_value = head[NEXT]
                head[NEXT] = old_next
                old_next[PREV] = head
                del mapping[old_key]
            last = tail[PREV]
            link = [last, tail, key, value]
            mapping[key] = last[NEXT] = tail[PREV] = link
        else:
            link_prev, link_next, key, value = link
            link_prev[NEXT] = link_next
            link_next[PREV] = link_prev
            last = tail[PREV]
            last[NEXT] = tail[PREV] = link
            link[PREV] = last
            link[NEXT] = tail
        return value

if __name__ == '__main__':
    p = LRU_Cache(pow, maxsize=3)
    for i in [1,2,3,4,5,3,1,5,1,1]:
        print(i, p(i, 2))
Run Code Online (Sandbox Code Playgroud)

  • 哇,你在很短的时间内为各种用户案例写了很多LRU,从python cookbook(activestate),python标准lib,blog,twitter或pycon讲座读取你的Python代码总是很高兴,现在开始stackoverflow也是. (2认同)
  • 它是完美的Pythonic - 一个简单的类,直接使用字典,列表,基本任务和解包.逻辑是自包含的,没有外部依赖.这段代码也非常快,并且可以通过PyPy轻松优化.OrderedDict增加了空间开销(它在内部使用两个dicts,而这只使用一个),并且它执行了此次使用不需要的不必要的工作.MutableMapping在此上下文中不提供任何有用的功能. (2认同)

Ian*_*hen 5

有很多好的答案,但我想指出一个简单的、Pythonic 的 LRU 缓存实现。这与亚历克斯·马尔泰利的回答类似。

from collections import OrderedDict, MutableMapping

class Cache(MutableMapping):
    def __init__(self, maxlen, items=None):
        self._maxlen = maxlen
        self.d = OrderedDict()
        if items:
            for k, v in items:
                self[k] = v

    @property
    def maxlen(self):
        return self._maxlen

    def __getitem__(self, key):
        self.d.move_to_end(key)
        return self.d[key]

    def __setitem__(self, key, value):
        if key in self.d:
            self.d.move_to_end(key)
        elif len(self.d) == self.maxlen:
            self.d.popitem(last=False)
        self.d[key] = value

    def __delitem__(self, key):
        del self.d[key]

    def __iter__(self):
        return self.d.__iter__()

    def __len__(self):
        return len(self.d)
Run Code Online (Sandbox Code Playgroud)