2013-05-09 4 views
17

Есть ли способ запоминать вывод функции на диск?memoize to disk - python - постоянная memoization

У меня есть функция

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

и хотел бы сделать что-то вроде:

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

, а затем вызвать getHtmlMemoized (URL), с тем, чтобы сделать дорогостоящие вычисления только один раз для каждого URL ,

+1

Просто разберите (или используйте json) тайник. – root

+1

спасибо, но я новичок python (второй день). Я не имею ни малейшего представления о том, что вы имеете в виду ... – seguso

+1

Хорошо, поэтому то, что вы делаете в качестве новичков, - это поиск пива «pickle python» в Google и возвращайтесь к нам, если у вас есть еще вопросы. –

ответ

20

Python предлагает очень элегантный способ сделать это s - декораторы. В принципе, декоратор - это функция, которая обертывает другую функцию для обеспечения дополнительной функциональности без изменения исходного кода функции. Ваш декоратор можно записать так:

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-сериализации.

Подробнее о декораторов: How to make a chain of function decorators?

А вот как сделать декоратор сохранить кэш только один раз, во время выхода:

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 
+3

Это будет записывать новый файл каждый раз, когда кеш обновляется - в зависимости от варианта использования это может (или не может) победить ускорение вы получаете из memoization .... – root

+1

это * также * содержит очень приятное условие гонки, если этот декоратор используется одновременно или (скорее), в порядке повторного входа. если 'a()' и 'b()' оба memoized и 'a()' вызывает 'b()', кеш может быть прочитан для 'a()', а затем снова для 'b()' , результат первого b будет замечен, но затем устаревший кеш от вызова до перезаписывает его, вклад b в кеш теряется. – SingleNegationElimination

+0

@root: Конечно, 'atexit', возможно, станет лучшим местом для очистки кеша. С другой стороны, добавление преждевременных оптимизаций может привести к поражению образовательной цели этого кода. – georg

0

Что-то вроде этого нужно сделать:

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') 
+0

выглядит отлично. но как использовать этот класс? извините ... – seguso

+1

@seguso - обновлено на использование. Подробнее о memoization: http://stackoverflow.com/questions/1988804/what-is-memoization-and-how-can-i-use-it-in-python – root

+0

-1 используя класс, если не нужно – Merlin

11

Заканчивать joblib.Memory. Это библиотека для этого.

+1

Всего три строки кода ! :) – Andrew

+1

Вау, какая отличная библиотека! Я не могу поверить, что все эти годы я не ездил без работы. Это должен быть правильный ответ ИМО. – foobarbecue

0

У этого Artemis library есть модуль для этого. (Вам нужно 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 

Внутренне, это делает хэш из входных аргументов и сохраняет мемо-файлы этого хэша.

0

Предполагая, что вы данные 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 

getHtmlOfUrl украшают, а затем просто вызвать его, если он был запущен ранее, вы получите ваши кэшированные данные.

Проверено с Python 2.x и 3.x питона

0

уборщик решение на базе модуля Полочка Python.Преимущество заключается в том, что кеш обновляется в реальном времени, что является доказательством исключения.

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 

Это облегчит вычисление функции только один раз. Следующие последующие вызовы для того же параметра возвращают сохраненный результат.