2013-05-09 8 views
17

Czy istnieje sposób na zapamiętanie danych wyjściowych funkcji na dysku?zapamiętaj na dysku - python - trwałe zapamiętywanie

Mam funkcji

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

i chciałbym zrobić coś takiego:

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

a następnie zadzwonić getHtmlMemoized (URL), tak aby zrobić drogiego obliczeń tylko raz dla każdego adresu URL .

+1

Wystarczy marynarka (lub użyj json) dyktować pamięć podręczną. – root

+1

dzięki, ale jestem początkującym pytonem (drugi dzień). Nie mam najmniejszego pojęcia, co masz na myśli ... – seguso

+1

Dobrze, więc to, co robisz jako początkujący, sprawdza w Google "pyton pikla" i wróć do nas, jeśli masz więcej pytań. –

Odpowiedz

20

Python oferuje bardzo elegancki sposób na zrobienie tego s - dekoratorzy. Zasadniczo dekorator jest funkcją, która owija inną funkcję w celu zapewnienia dodatkowej funkcjonalności bez zmiany kodu źródłowego funkcji. Twój dekorator można zapisać tak:

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 

Gdy masz, że „ozdobić” funkcję używając @ -syntax i jesteś gotowy.

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

Zauważ, że ten dekorator jest celowo uproszczone i nie mogą pracować w każdej sytuacji, na przykład, gdy funkcja zwraca źródło akceptuje lub dane, których nie można json-odcinkach.

Więcej na dekoratorów: How to make a chain of function decorators?

A oto jak zrobić dekorator zaoszczędzić pamięć podręczną tylko raz, w momencie wyjścia:

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

Spisze to nowy plik za każdym razem, gdy zostanie zaktualizowana pamięć podręczna - w zależności od przypadku użycia może to (lub nie) pokonać przyspieszenie otrzymujesz z zapamiętywania ... – root

+1

it * also * zawiera bardzo ładny wyścigowy warunek, jeśli ten dekorator jest używany jednocześnie, lub (bardziej prawdopodobne), w trybie ponownego wprowadzenia. jeśli 'a()' i 'b()' są zapamiętane, a 'a()' wywołania 'b()', pamięć podręczną można odczytać dla 'a()', a następnie ponownie dla 'b()' , wynik pierwszego b jest pamiętany, ale potem nieaktualny bufor z połączenia do nadpisuje, wkład b do pamięci podręcznej zostaje utracony. – SingleNegationElimination

+0

@root: na pewno, 'atexit' będzie prawdopodobnie lepszym miejscem do spłukiwania pamięci podręcznej. Z drugiej strony, dodanie przedwczesnych optymalizacji może pokonać edukacyjny cel tego kodu. – georg

0

Coś jak to powinno zrobić:

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] 

Podstawowe użycie:

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

Jeśli chcesz napisać swój "cache" do pliku po użyciu go - być ponownie ładowane w przyszłości:

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

wygląda świetnie. ale jak korzystać z tej klasy? przepraszam ... – seguso

+1

@seguso - zaktualizowałam użycie. Więcej na temat zapamiętywania: http://stackoverflow.com/questions/1988804/what-is-memoization-and-how-can-i-use-it-in-python – root

+0

-1 używanie klasy, gdy nie jest potrzebne – Merlin

11

Wyjazd joblib.Memory. Jest to biblioteka do robienia właśnie tego.

+1

Tylko trzy linie kodu ! :) – Andrew

+1

Wow, co za wspaniała biblioteka! Nie mogę uwierzyć, że minęły te wszystkie lata bez pracy. To powinna być poprawna odpowiedź IMO. – foobarbecue

0

Moduł Artemis library ma do tego moduł. (Musisz pip install artemis-ml)

udekorować swoją funkcję:

from artemis.fileman.disk_memoize import memoize_to_disk 

@memoize_to_disk 
def fcn(a, b, c = None): 
    results = ... 
    return results 

Wewnętrznie, to sprawia, że ​​hash z argumentów wejściowych i zapisuje pliki Memo przez tego skrótu.

0

Zakładając, że dane JSON serializacji, ten kod powinien działać

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 

ozdobić getHtmlOfUrl a następnie po prostu nazwać, gdyby został uruchomiony wcześniej, można uzyskać dostęp do danych w pamięci podręcznej.

sprawdzone z pytona 2.x i pytona 3.x

0

Czyszczenia roztwór zasilany przez moduł półek Pythona.Zaletą jest aktualizowanie pamięci podręcznej w czasie rzeczywistym, co stanowi wyjątek.

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 

Ułatwi to obliczenie funkcji tylko raz. Następne kolejne wywołania tego samego parametru zwrócą zapisany wynik.