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)

Author: Community, 2015-11-12

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.

 22
Author: orlp,
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