明らかに、すばやく検索すると、Python のメモ化デコレーターの実装とフレーバーが 100 万個も見つかります。しかし、まだ見つけられていないフレーバーに興味があります。保存された値のキャッシュが固定容量になるようにしたいと思います。新しい要素が追加され、容量に達した場合、最も古い値が削除され、最新の値に置き換えられます。
私の懸念は、メモ化を使用して非常に多くの要素を保存すると、メモリ不足のためにプログラムがクラッシュすることです。(この懸念が実際にどれほど適切に配置されているかはわかりません。) キャッシュのサイズが固定されている場合、メモリ エラーは問題になりません。そして、私が取り組んでいる多くの問題は、プログラムが実行されるにつれて変化するため、最初にキャッシュされた値は、後でキャッシュされた値とは大きく異なって見えます (そして、後で再発する可能性ははるかに低くなります)。だから、古いものは新しいものに置き換えてほしい。
OrderedDict
クラスと、それをサブクラス化して最大サイズを指定する方法を示す例を見つけました。通常のキャッシュではなく、それをキャッシュとして使用したいと思いますdict
。問題は、 memoize デコレータmaxlen
がデフォルトでと呼ばれるパラメータを取る必要があることNone
です。である場合None
、キャッシュは無限であり、通常どおりに動作します。その他の値は、キャッシュのサイズとして使用されます。
私はそれが次のように動作することを望みます:
@memoize
def some_function(spam, eggs):
# This would use the boundless cache.
pass
と
@memoize(200) # or @memoize(maxlen=200)
def some_function(spam, eggs):
# This would use the bounded cache of size 200.
pass
以下は私がこれまでに持っているコードですが、「ネイキッド」とパラメーターの両方を機能させながら、パラメーターをデコレーターに渡す方法がわかりません。
import collections
import functools
class BoundedOrderedDict(collections.OrderedDict):
def __init__(self, *args, **kwds):
self.maxlen = kwds.pop("maxlen", None)
collections.OrderedDict.__init__(self, *args, **kwds)
self._checklen()
def __setitem__(self, key, value):
collections.OrderedDict.__setitem__(self, key, value)
self._checklen()
def _checklen(self):
if self.maxlen is not None:
while len(self) > self.maxlen:
self.popitem(last=False)
def memoize(function):
cache = BoundedOrderedDict() # I want this to take maxlen as an argument
@functools.wraps(function)
def memo_target(*args):
lookup_value = args
if lookup_value not in cache:
cache[lookup_value] = function(*args)
return cache[lookup_value]
return memo_target
@memoize
def fib(n):
if n < 2: return 1
return fib(n-1) + fib(n-2)
if __name__ == '__main__':
x = fib(50)
print(x)
編集:ベンの提案を使用して、次のデコレータを作成しました。これは、私が想像したとおりに機能すると信じています。これらの装飾された関数を で使用できることは私にとって重要でmultiprocessing
あり、これは過去の問題でした。しかし、このコードの簡単なテストでは、ジョブをスレッドのプールにファームアウトした場合でも、正しく動作するように見えました。
def memoize(func=None, maxlen=None):
if func:
cache = BoundedOrderedDict(maxlen=maxlen)
@functools.wraps(func)
def memo_target(*args):
lookup_value = args
if lookup_value not in cache:
cache[lookup_value] = func(*args)
return cache[lookup_value]
return memo_target
else:
def memoize_factory(func):
return memoize(func, maxlen=maxlen)
return memoize_factory