Memoize to disk - python - постоянная memoization
Есть ли способ запоминать вывод функции на диск?
У меня есть функция
def getHtmlOfUrl(url):
... # expensive computation
и хотел бы сделать что-то вроде:
def getHtmlMemoized(url) = memoizeToFile(getHtmlOfUrl, "file.dat")
а затем вызовите getHtmlMemoized (url), чтобы выполнить дорогостоящее вычисление только один раз для каждого URL-адреса.
Ответы
Ответ 1
Python предлагает очень элегантный способ сделать это - декораторы. В принципе, декоратор - это функция, которая обертывает другую функцию для обеспечения дополнительной функциональности без изменения исходного кода функции. Ваш декоратор можно написать следующим образом:
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
Как только у вас получится, "украсить" функцию, используя @-syntax, и вы готовы.
@persist_to_file('cache.dat')
def html_of_url(url):
your function code...
Обратите внимание, что этот декоратор намеренно упрощен и может не работать для каждой ситуации, например, когда исходная функция принимает или возвращает данные, которые не могут быть сериализованы json.
Подробнее о декораторах: Как создать цепочку декораторов функций?
И вот как сделать декоратор сохранить кеш только один раз, во время выхода:
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
Ответ 2
Проверьте joblib.Memory
. Это библиотека для именно этого.
Ответ 3
Более чистое решение на базе модуля Python Shelve. Преимущество заключается в том, что кэш обновляется в режиме реального времени с помощью хорошо известного синтаксиса dict
, а также является доказательством исключений (нет необходимости обрабатывать надоедливую KeyError
).
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
Это облегчит вычисление функции только один раз. Следующие последующие вызовы вернут сохраненный результат.
Ответ 4
Что-то вроде этого должно делать:
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]
Основное использование:
your_mem_func = Memoize(your_func)
your_mem_func.load_memo('yourdata.json')
# do your stuff with your_mem_func
Если вы хотите записать свой "кеш" в файл после его использования - для повторного загрузки в будущем:
your_mem_func.save_memo('yournewdata.json')
Ответ 5
Библиотека Artemis имеет для этого модуль. (вам нужно pip install artemis-ml
)
Вы украшаете свою функцию:
from artemis.fileman.disk_memoize import memoize_to_disk
@memoize_to_disk
def fcn(a, b, c = None):
results = ...
return results
Внутренне это делает хэш из входных аргументов и сохраняет memo файлы с помощью этого хэша.
Ответ 6
Предполагая, что данные json сериализуемы, этот код должен работать
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
, а затем просто назовите его, если он был запущен ранее, вы получите свои кэшированные данные.
Проверено с помощью python 2.x и python 3.x
Ответ 7
Вы можете использовать пакет 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
Это будет кэшировать результаты в течение 3 дней, специфичные для аргументов a, b, c и d. Результаты сохраняются в файле pickle на вашем компьютере, а затем выбираются и возвращаются при следующем вызове функции. Через 3 дня файл рассола будет удален до повторного запуска функции. Функция будет перезапускаться всякий раз, когда функция вызывается с новыми аргументами. Более подробная информация здесь: https://github.com/sarenehan/cache_to_disk