metodos - importar clases en python
Functools de Python lru_cache con métodos de clase: liberar objeto (1)
¿Cómo puedo usar functools ''lru_cache dentro de las clases sin pérdida de memoria? En el siguiente ejemplo mínimo, la instancia de foo
no se lanzará aunque se salga del alcance y no tenga referencia (excepto la caché lru).
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()
Pero foo
y por foo.big
tanto foo.big
(una gran BigClass
) siguen vivos
import gc; gc.collect() # collect garbage
len([obj for obj in gc.get_objects() if isinstance(obj, Foo)]) # is 1
Eso significa que las instancias de Foo / BigClass aún residen en la memoria. Incluso la eliminación de Foo
(del Foo
) no los liberará.
¿Por qué lru_cache se aferra a la instancia? ¿El caché no usa el hash y no el objeto real?
¿Cuál es la forma recomendada de usar lru_caches dentro de las clases?
Sé de dos soluciones: utilizar cachés por instancia o hacer que la memoria caché ignore el objeto (lo que podría dar lugar a resultados erróneos)
Esta no es la solución más limpia, pero es completamente transparente para el programador:
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
Toma exactamente los mismos parámetros que lru_cache
, y funciona exactamente igual. Sin embargo, nunca pasa self
a lru_cache
y en su lugar utiliza un lru_cache
por instancia.