python memoization

memoize to disk-python-memoization persistente



(7)

¿Hay alguna forma de memorizar la salida de una función al disco?

Tengo una función

def getHtmlOfUrl(url): ... # expensive computation

y me gustaría hacer algo como:

def getHtmlMemoized(url) = memoizeToFile(getHtmlOfUrl, "file.dat")

y luego llame a getHtmlMemoized (url), para hacer el cálculo costoso solo una vez por cada url.


Algo como esto debería hacer:

import json class Memoize(object): def __init__(self, func): self.func = func self.memo = {} def load_memo(filename): with open(filename) as f: self.memo.update(json.load(f)) def save_memo(filename): with open(filename, ''w'') as f: json.dump(self.memo, f) def __call__(self, *args): if not args in self.memo: self.memo[args] = self.func(*args) return self.memo[args]

Uso básico:

your_mem_func = Memoize(your_func) your_mem_func.load_memo(''yourdata.json'') # do your stuff with your_mem_func

Si desea escribir su "caché" en un archivo después de usarlo, para volver a cargarlo en el futuro:

your_mem_func.save_memo(''yournewdata.json'')


Echa un vistazo a joblib.Memory . Es una biblioteca para hacer exactamente eso.


La biblioteca de Artemisa tiene un módulo para esto. (tendrás que pip install artemis-ml )

Tú decoras tu función:

from artemis.fileman.disk_memoize import memoize_to_disk @memoize_to_disk def fcn(a, b, c = None): results = ... return results

Internamente, hace un hash de los argumentos de entrada y guarda archivos memo por este hash.


Puedes usar el paquete cache_to_disk:

from cache_to_disk import cache_to_disk @cache_to_disk(3) def my_func(a, b, c, d=None): results = ... return results

Esto almacenará en caché los resultados durante 3 días, específicos de los argumentos a, b, c y d. Los resultados se almacenan en un archivo pickle en su máquina, y se seleccionan y devuelven la próxima vez que se llama a la función. Después de 3 días, el archivo pickle se elimina hasta que la función se vuelve a ejecutar. La función se volverá a ejecutar siempre que se llame a la función con nuevos argumentos. Más información aquí: https://github.com/sarenehan/cache_to_disk


Python ofrece una forma muy elegante de hacer esto: decoradores. Básicamente, un decorador es una función que envuelve otra función para proporcionar funcionalidad adicional sin cambiar el código fuente de la función. Tu decorador puede escribirse así:

import json def persist_to_file(file_name): def decorator(original_func): try: cache = json.load(open(file_name, ''r'')) except (IOError, ValueError): cache = {} def new_func(param): if param not in cache: cache[param] = original_func(param) json.dump(cache, open(file_name, ''w'')) return cache[param] return new_func return decorator

Una vez que tengas eso, ''decora'' la función usando @ -syntax y estás listo.

@persist_to_file(''cache.dat'') def html_of_url(url): your function code...

Tenga en cuenta que este decorador está simplificado intencionalmente y puede que no funcione en todas las situaciones, por ejemplo, cuando la función de origen acepta o devuelve datos que no pueden ser serializados por json.

Más sobre decoradores: ¿Cómo hacer una cadena de decoradores funcionales?

Y aquí es cómo hacer que el decorador guarde el caché solo una vez, al momento de la salida:

import json, atexit def persist_to_file(file_name): try: cache = json.load(open(file_name, ''r'')) except (IOError, ValueError): cache = {} atexit.register(lambda: json.dump(cache, open(file_name, ''w''))) def decorator(func): def new_func(param): if param not in cache: cache[param] = func(param) return cache[param] return new_func return decorator


Suponiendo que sus datos son serializables json, este código debería funcionar

import os, json def json_file(fname): def decorator(function): def wrapper(*args, **kwargs): if os.path.isfile(fname): with open(fname, ''r'') as f: ret = json.load(f) else: with open(fname, ''w'') as f: ret = function(*args, **kwargs) json.dump(ret, f) return ret return wrapper return decorator

decorate getHtmlOfUrl y luego simplemente getHtmlOfUrl , si se ejecutó anteriormente, obtendrá sus datos en caché.

Comprobado con python 2.xy python 3.x


Una solución más limpia impulsada por el módulo Shelve de Python. La ventaja es que la memoria caché se actualiza en tiempo real sin una sintaxis dict conocida, también es una prueba de excepción (no hay necesidad de manejar KeyError molesto).

import shelve def shelve_it(file_name): d = shelve.open(file_name) def decorator(func): def new_func(param): if param not in d: d[param] = func(param) return d[param] return new_func return decorator @shelve_it(''cache.shelve'') def expensive_funcion(param): pass

Esto facilitará que la función se calcule solo una vez. Las siguientes llamadas subsiguientes para el mismo parámetro devolverán el resultado almacenado.