Python3.2で
以降、あなたがfunctools.lru_cache
を使用することができます。 total
をfunctools.lru_cache
に直接飾る場合、lru_cache
は、両方の引数の値に基づいて、self
とx
の両方の値に基づいて、戻り値をtotal
としてキャッシュします。 lru_cacheの内部dictはselfへの参照を格納するので、@lru_cacheをクラスメソッドに直接適用すると、クラスのインスタンスを参照不可能にする(したがってメモリリークを)する循環参照がself
になります。
import functools
import weakref
def memoized_method(*lru_args, **lru_kwargs):
"""
https://stackoverflow.com/a/33672499/190597 (orly)
"""
def decorator(func):
@functools.wraps(func)
def wrapped_func(self, *args, **kwargs):
# We're storing the wrapped method inside the instance. If we had
# a strong reference to self the instance would never die.
self_weak = weakref.ref(self)
@functools.wraps(func)
@functools.lru_cache(*lru_args, **lru_kwargs)
def cached_method(*args, **kwargs):
return func(self_weak(), *args, **kwargs)
setattr(self, func.__name__, cached_method)
return cached_method(*args, **kwargs)
return wrapped_func
return decorator
class MyObject(object):
def __init__(self, a, b):
self.a, self.b = a, b
@memoized_method()
def total(self, x):
print('Calling total (x={})'.format(x))
return (self.a + self.b) * x
def subtotal(self, y, z):
return self.total(x=y) + z
mobj = MyObject(1,2)
mobj.subtotal(10, 20)
mobj.subtotal(10, 30)
プリント:それは最初の1、self
以外のすべての引数に基づいて結果をキャッシュし、循環参照の問題を回避するためにweakrefを使用しています - あなたはクラスメソッドでlru_cache
を使用することができます
Here is a workaround
Calling total (x=10)
一度だけ。
また、これはあなたが辞書を使用して独自のキャッシュをロールバックできる方法である:この辞書ベースのキャッシュを超えるlru_cache
の
class MyObject(object):
def __init__(self, a, b):
self.a, self.b = a, b
self._total = dict()
def total(self, x):
print('Calling total (x={})'.format(x))
self._total[x] = t = (self.a + self.b) * x
return t
def subtotal(self, y, z):
t = self._total[y] if y in self._total else self.total(y)
return t + z
mobj = MyObject(1,2)
mobj.subtotal(10, 20)
mobj.subtotal(10, 30)
一つの利点は、lru_cache
は、スレッドセーフであるということです。には、 長時間実行中のプロセスがtotal
を何度も呼び出して、異なる値x
を呼び出しているなどの理由で、メモリ使用量が制限されて増加するのを防ぐのに役立つmaxsize
パラメータもあります。
これを試しましたか:http://stackoverflow.com/a/18723434/2570677私は自分のコードでそれを使用し、それは完全に動作します。 –
あなたは '@ functools.lru_cache'を参照していると仮定しますか? –
あなたがリンクしているリソースから、基本的なキャッシュ機能で 'total'を装飾することを止めているのは何ですか?あなたは '@ functools.lru_cache(maxsize = N)'を置くだけで、同じ引数に対して 'N'の結果をキャッシュします。なぜあなたのシナリオではうまくいかないのですか? –