¿Hay alguna forma canónica de almacenar en caché los métodos de instancia en Python?

9

Tengo algunas funciones computacionalmente intensivas en mi script de Python que me gustaría almacenar en caché. Busqué soluciones en el desbordamiento de pila y encontré muchos enlaces:

  1. /programming/4431703/python-resettable-instance-method-memoization-decorator
  2. https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
  3. http://pythonhosted.org/cachetools/
  4. https://pythonhosted.org/Flask-Cache/ (he usado este para aplicaciones de matraz, pero este no es un matraz).

Al final, terminé pegando esto en mi programa. Parece bastante simple, y funciona bien.

class memoized(object):
    '''Decorator. Caches a function's return value each time it is called.
    If called later with the same arguments, the cached value is returned
    (not reevaluated).
    '''
    def __init__(self, func):
        self.func = func
        self.cache = {}

    def __call__(self, *args):
        if not isinstance(args, collections.Hashable):
            return self.func(*args)
        if args in self.cache:
            return self.cache[args]
        else:
            value = self.func(*args)
            self.cache[args] = value
            return value

    def __repr__(self):
        '''Return the function's docstring.'''
        return self.func.__doc__

    def __get__(self, obj, objtype):
        '''Support instance methods.'''
        return functools.partial(self.__call__, obj)

Sin embargo, me pregunto si hay una mejor práctica canónica en Python. Supongo que supuse que habría un paquete muy utilizado para manejar esto y estoy confundido acerca de por qué esto no existe. http://pythonhosted.org/cachetools/ solo está en la versión .6 y la sintaxis es más compleja que simplemente agregar un decorador @memoize, como en otras soluciones.

bernie2436
fuente

Respuestas:

12

No existe una forma canónica, únicamente pitónica, de hacerlo. De todos modos, no tengo conocimiento de nada, y estoy hablando como alguien que ha mirado y que es el autor de un paquete de memoria exitoso .

Sin embargo, creo que su falta de arte previo encontrado puede ser un problema de terminología más que nada. Usted pidió almacenamiento en caché . Ese es un término apropiado, pero es demasiado amplio. El almacenamiento en caché de los resultados de una llamada de función o actividad particular para su uso posterior se conoce más específicamente como memorización o memorización . Y, de hecho, hay muchos paquetes memorables disponibles de la comunidad , así como muchas recetas ( por ejemplo, esta ). También he visto funciones de memoria en muchos paquetes de utilidad multipropósito. Muchos de ellos son maduros, endurecidos para la batalla y se usan constantemente en la producción, no solo el "código de la versión 0.6".

No puedo decir por qué las memorias no se manejan de manera más canónica o idiomática. Quizás porque hay varias formas de lograrlo con diferentes virtudes y compensaciones. O tal vez porque ya hay tantos enfoques diferentes en uso. A menudo encuentro características ("aplanar una lista de listas" es otra) en las que otras comunidades lingüísticas convergen con entusiasmo, pero que la comunidad de Python o los poderes que parecen preferir manejar como recetas en lugar de comprometerse con una API específica. En cualquier caso, si su código funciona, ¡bienvenido a las filas de memorizadores exitosos!

Actualizar

Dado que la biblioteca estándar de Python 3 (para 3.2 y posteriores) incluye un lru_cachedecorador ( documentación aquí ), debo decir que parece un intento de última hora para estandarizar el caso de uso de memorización más común. El hecho de que haya llegado tan tarde en la evolución de Python es probablemente la razón por la que no hay una solución común, pero para el nuevo código, eso es lo más canónico que encontrarás.

Jonathan Eunice
fuente
1
Python 3.8 agregó el @cached_propertydecorador , que se adapta mejor a los métodos cuyos valores de retorno se mantendrán iguales durante toda la vida de la instancia.
Patrick Brinich-Langlois
0

Debido a que un método de instancia puede usar atributos propios y, en particular, modificarlos, no puede garantizar la corrección de un método de instancia memorable arbitrario. Además, su implementación agrega una restricción implícita en su objeto para que sea hashable, y el acierto de caché dependiendo de ese hash, está limitado en las clases en las que puede usarlo y en los campos que puede agregar a las clases que tienen un método memorizado (o heredar uno).

Por estas razones, si necesita memorizar una función, es mejor diseñar convertir el método de instancia costoso en un método estático, pasándole explícitamente los atributos de objeto necesarios como argumentos. Esto libera tus manos en el diseño de clase y puede mejorar el éxito de la memoria caché. Esto también ayuda a simplificar y generalizar el código de memorización. Existen implementaciones de grano fino para este diseño, que a veces le permiten personalizar el tamaño de la memoria caché, la duración / métodos de invalidación, garantizando la seguridad del hilo, la persistencia ...

Arthur Havlicek
fuente