python 如何限制字典的大小?

xe55xuns  于 2023-06-20  发布在  Python
关注(0)|答案(8)|浏览(169)

我想在python中使用dict,但将键/值对的数量限制为X。换句话说,如果dict当前存储了X个键/值对,并且我执行了插入,我希望删除其中一个现有的键/值对。如果它是最近插入/访问的密钥就好了,但这并不是完全必要的。
如果这存在于标准库中,请保存我一些时间,并指出它!

h9a6wy2h

h9a6wy2h1#

Python 2.7和3.1有OrderedDict,并且有早期Python的纯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)

您还必须重写其他可以插入项的方法,例如updateOrderedDict的主要用途是让您可以轻松控制弹出的内容,否则普通的dict就可以工作。

slmsl1lt

slmsl1lt2#

cachetools将为您提供一个很好的Map哈希实现(它可以在python 2和3上工作)。
文件摘录:
在本模块中,缓存是固定最大大小的可变Map。当该高速缓存已满时,即通过添加另一项该高速缓存将超过其最大大小,高速缓存必须基于合适的高速缓存算法来选择丢弃哪些项。

ca1c2owp

ca1c2owp3#

这里有一个简单的,无LRU的Python 2.6+解决方案(在旧版Python中,你可以用UserDict.DictMixin做类似的事情,但在2.6及更高版本中,不推荐这样做,而且collections的ABC更可取...):

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))

正如其他答案所提到的,你可能不想子类化dict --不幸的是,对self.d的显式委托是一个样板,但它确实保证了collections.MutableMapping正确地提供了所有其他方法。

acruukt9

acruukt94#

下面是一个简单而高效的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))
ni65a41a

ni65a41a5#

已经有很多很好的答案,但我想指出一个简单的,pythonic的LRU缓存实现。这与Alex Martelli的回答相似。

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)
ruyhziif

ruyhziif6#

您可以通过派生dict的子类来创建自定义字典类。在您的例子中,您必须重写__setitem__来检查您自己的长度,并在限制被重新缓存时删除一些内容。下面的示例将在每次插入后打印当前长度:

class mydict(dict):
    def __setitem__(self, k, v):
        dict.__setitem__(self, k, v)
        print len(self)

d = mydict()
d['foo'] = 'bar'
d['bar'] = 'baz'
6qfn3psc

6qfn3psc7#

dict没有此行为。您可以创建自己的类来执行此操作,例如

class MaxSizeDict(object):
    def __init__(self, max_size):
        self.max_size = max_size
        self.dict = {}
    def __setitem__(self, key, value):
        if key in self.dict:
            self.dict[key] = value    
            return

        if len(self.dict) >= self.max_size:
      ...

关于这一点的几点说明

  • 对于一些人来说,在这里子类化dict是很有诱惑力的。从技术上讲,您可以做到这一点,但它容易出错,因为这些方法彼此不依赖。您可以使用UserDict.DictMixin来保存定义所有方法。如果您子类化dict,则可以重用的方法很少。
  • 字典不知道最近添加的键是什么,因为字典是无序的。
  • Python 2.7将引入collections.OrderedDict,但现在,将键分别按顺序保存应该可以正常工作(使用collections.deque作为队列)。
  • 如果获取最旧的并不那么重要,那么您可以使用popitem方法删除任意一个项。
  • 我把最老解释为第一次插入,大约。你必须做一些不同的事情来消除LRU项。最明显的有效策略是保持一个键的双向链表,其中包含对节点本身的引用,并将其存储为dict值(沿着真实的值)。这变得更加复杂,并且在纯Python中实现它会带来大量开销。
gtlvzcf8

gtlvzcf88#

有一个名为CircularDict的库实现了这种行为。它允许限制dict可以存储的最大项目量,但也可以设置内存使用限制。
它可以安装有:

pip install circular-dict

并以这种方式使用:

from circular_dict import CircularDict

# Initialize a CircularDict with a maximum length of 3
my_dict = CircularDict(maxlen=3) # You could also set maxsize_bytes=8*1024 bytes

# Fill it with 4 items
my_dict['item1'] = 'value1'
my_dict['item2'] = 'value2'
my_dict['item3'] = 'value3'
# When adding this 4th item, the 1st one will be dropped
my_dict['item4'] = 'value4'
print(circ_dict)

Ouptut会看起来像。

{'item2': 'value2', 'item3': 'value3', 'item4': 'value4'}

相关问题