2015-11-12 9 views
17

functoolsのlru_cacheをクラスの中でメモリリークなしで使用するにはどうすればよいですか? 以下の最小限の例では、範囲外になり、リファラー(lru_cache以外)がなくても、fooインスタンスは解放されません。クラスメソッドを持つPython functools lru_cache:releaseオブジェクト

from functools import lru_cache 
class BigClass: 
    pass 
class Foo: 
    def __init__(self): 
     self.big = BigClass() 
    @lru_cache(maxsize=16) 
    def cached_method(self, x): 
     return x + 5 

def fun(): 
    foo = Foo() 
    print(foo.cached_method(10)) 
    print(foo.cached_method(10)) # use cache 
    return 'something' 

fun() 

しかしfooひいてはfoo.bigBigClass)は依然としてはFoo/BigClassインスタンスがまだメモリ内に存在していることを意味する

import gc; gc.collect() # collect garbage 
len([obj for obj in gc.get_objects() if isinstance(obj, Foo)]) # is 1 

生きています。 Foo(del Foo)を削除しても、それらは解放されません。

lru_cacheがインスタンスを保持しているのはなぜですか?キャッシュは実際のオブジェクトではなくハッシュを使用していませんか?

クラス内でlru_cachesを使用する推奨される方法は何ですか?

私は2つの回避策を知っている:これは、クリーンなソリューションではありませんが、それは、プログラマに対して完全に透過的です Use per instance cachesまたはmake the cache ignore object(ただし、間違った結果になるかもしれません)

答えて

16

import functools 
import weakref 

def memoized_method(*lru_args, **lru_kwargs): 
    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 

それはlru_cacheとまったく同じパラメータをとり、まったく同じ働きをします。ただし、selflru_cacheに決して渡されず、インスタンスごとにlru_cacheが使用されます。

関連する問題