Python functools LRU cache with class methods: release object
Jak mogę używać lru_cache functools wewnątrz klas bez wycieku pamięci?
W poniższym minimalnym przykładzie instancja foo
nie zostanie zwolniona, chociaż nie będzie miała żadnego referrera (innego niż lru_cache).
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()
Ale foo
i stąd foo.big
(a BigClass
) wciąż żyją
import gc; gc.collect() # collect garbage
len([obj for obj in gc.get_objects() if isinstance(obj, Foo)]) # is 1
Oznacza to, że instancje Foo/BigClass nadal pozostają w pamięci. Nawet usunięcie Foo
(del Foo
) ich nie wyda.
Dlaczego lru_cache trzyma instancję w ogóle? Czy pamięć podręczna nie używa jakiegoś hasha, a nie rzeczywistego obiektu?
Jaki jest zalecany sposób użycia lru_caches wewnątrz klas?
Znam dwa obejścia: użyj pamięci podręcznej dla każdej instancji lub Ustaw obiekt ignorujący pamięć podręczną (co może prowadzić do błędnych wyników)
1 answers
Nie jest to najczystsze rozwiązanie, ale jest całkowicie przejrzyste dla programisty:
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
Pobiera dokładnie te same parametry co lru_cache
i działa dokładnie tak samo. Jednak nigdy nie przechodzi self
do lru_cache
i zamiast tego używa instancji lru_cache
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-12 15:40:43