Ограничение размера словаря python
Я хотел бы работать с dict в python, но ограничить количество пар ключ/значение X. другими словами, если dict в настоящее время хранит пары ключ/значение X, и я выполняю вставку, я хотел бы, чтобы одна из существующих пар была удалена. Было бы неплохо, если бы это был наименее недавно вставленный/доступ к ключу, но это не совсем необходимо.
Если это существует в стандартной библиотеке, пожалуйста, сэкономьте мне время и укажите это!
7 ответов
Python 2.7 и 3.1 имеют OrderedDict и есть реализации pure-Python для более ранних Pythons.
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)
вам также придется переопределить другие методы, которые могут вставлять элементы, такие как update. Основное использование OrderedDict, так что вы можете контролировать то, что получает выскочил легко, в противном случае нормальный дикт будет работать.
cachetools предоставит вам хорошую реализацию хэшей отображения, которая делает это (и она работает на python 2 и 3).
выдержка из документации:
для этого модуля кэш является изменяемым отображением фиксированного максимальный размер. Когда кэш заполнен, т. е. путем добавления другого элемента, кэш превысит свой максимальный размер, кэш должен выбрать, какой элемент(ы) чтобы отказаться от зависимости от соответствующей кэша алгоритм.
вот простое, не - LRU Python 2.6 + решение (в старых Pythons вы можете сделать что-то подобное с UserDict.DictMixin
, но в 2.6 и лучше это не рекомендуется, а Азбука от collections
в любом случае предпочтительнее...):
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 -- явное делегирование себя.d, К сожалению, boilerplatey, но это делает гарантия что каждый другой метод правильно поставлен collections.MutableDict
.
вот простой и эффективный кэш LRU, написанный с помощью простого кода Python dirt, который работает на любой версии 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))
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
. - дикт не знает, что наименее недавно добавленный ключ, так как дикты неупорядочены.
- 2.7 познакомим
collections.OrderedDict
, но пока держать ключи в порядке отдельно должно работать отлично (используйтеcollections.deque
в очереди). - если старые еще не все, что важно, вы можете просто использовать
popitem
метод удаления одного произвольного элемента.
- 2.7 познакомим
- я interprettered старейших означать первой вставки, примерно. Вы пришлось бы сделать что-то немного другое, чтобы устранить элементы LRU. Наиболее очевидной эффективной стратегией было бы сохранение двусвязного списка ключей со ссылками на сами узлы, хранящиеся как значения dict (наряду с реальными значениями). Это становится более сложным, и реализация его в чистом Python несет много накладных расходов.
вы можете создать пользовательский класс словаря путем подкласса dict. В вашем случае вам придется переопределить __setitem__
чтобы проверить вашу собственную длину и удалить что-то если лимит recahed. Следующий пример будет печатать текущую длину после каждой вставки:
class mydict(dict):
def __setitem__(self, k, v):
dict.__setitem__(self, k, v)
print len(self)
d = mydict()
d['foo'] = 'bar'
d['bar'] = 'baz'
там было много хороших ответов, но я хочу отметить простой, обновления для реализации 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)