62

Python で dict を使用したいのですが、キーと値のペアの数を X に制限します。つまり、dict が現在 X のキーと値のペアを格納していて、挿入を実行する場合、次のいずれかが必要です。ドロップする既存のペア。それが最も最近挿入された/アクセスされていないキーであればいいのですが、それは完全に必要というわけではありません。

これが標準ライブラリに存在する場合は、時間を節約して指摘してください!

4

7 に答える 7

49

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)

また、など、アイテムを挿入できる他のメソッドをオーバーライドする必要がありますupdate。の主な用途はOrderedDict、ポップされるものを簡単に制御できるようにすることです。そうしないと、通常dictは機能します。

于 2010-03-13T07:32:35.343 に答える
9

Here is a simple and efficient LRU cache written with dirt simple Python code that runs on any python version 1.5.2 or later:

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))
于 2011-11-30T23:49:34.813 に答える
2

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'
于 2010-03-13T07:28:45.227 に答える
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
  • dictは順序付けられていないため、dictは最後に追加されたキーが何であるかを知りません。
    • 2.7で導入されcollections.OrderedDictますが、今のところ、キーを個別に並べておくと正常に機能するはずです(collections.dequeキューとしてaを使用してください)。
    • 最も古いものを取得することがそれほど重要ではない場合は、このpopitemメソッドを使用して任意の1つのアイテムを削除できます。
  • 私は最も古いものを最初の挿入を意味すると解釈しました。LRUアイテムを削除するには、少し異なることを行う必要があります。最も明白な効率的な戦略は、(実際の値とともに)dict値として保存されたノード自体への参照を含む二重にリンクされたキーのリストを保持することを含みます。これはより複雑になり、純粋なPythonで実装すると多くのオーバーヘッドが発生します。
于 2010-03-13T07:28:48.533 に答える