2015-11-12 19 views
17

Come posso usare functools 'lru_cache all'interno delle classi senza perdite di memoria? Nell'esempio seguente, l'istanza foo non verrà rilasciata sebbene esca dall'ambito e non abbia alcun referrer (diverso da lru_cache).functools Python lru_cache con i metodi di classe: oggetto 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() 

Ma foo e quindi foo.big (a BigClass) sono ancora vivi

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

Ciò significa che le istanze Esempio/BigClass continuano a risiedere in memoria. Anche l'eliminazione di (del) non li rilascia.

Perché lru_cache è ancora presente sull'istanza? La cache non usa qualche hash e non l'oggetto reale?

Qual è il modo consigliato di utilizzare lru_caches all'interno delle classi?

So di due soluzioni: Use per instance caches o make the cache ignore object (che potrebbe condurre a risultati sbagliati, però)

risposta

16

Questa non è la soluzione più pulita, ma è del tutto trasparente per il programmatore:

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 

Prende gli stessi identici parametri di lru_cache e funziona esattamente allo stesso modo. Tuttavia non passa mai self a lru_cache e utilizza invece l'istanza lru_cache.

Problemi correlati