tool - python reduce package



Python functools lru_cache avec les méthodes de classe: objet de version (1)

Ce n'est pas la solution la plus propre, mais elle est entièrement transparente pour le programmeur:

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

Il prend exactement les mêmes paramètres que lru_cache , et fonctionne exactement de la même manière. Cependant, il ne passe jamais à lru_cache et utilise à la place lru_cache par instance.

Comment puis-je utiliser lru_cache de functools dans les classes sans fuites de mémoire? Dans l'exemple minimal suivant, l'instance de foo ne sera pas libérée même si elle sort de la portée et n'a pas de référent (autre que 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()

Mais foo et donc foo.big (une BigClass ) sont toujours vivants

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

Cela signifie que les instances Foo / BigClass résident toujours en mémoire. Même la suppression de Foo (del Foo ) ne les libèrera pas.

Pourquoi lru_cache reste-t-il sur l'instance? Le cache n'utilise-t-il pas de hachage et pas l'objet réel?

Quelle est la manière recommandée d'utiliser lru_caches à l'intérieur des classes?

Je connais deux solutions de contournement: Utiliser des caches d'instance ou rendre l'objet ignorer le cache (ce qui peut conduire à de mauvais résultats, cependant)





functools