2013-03-23 17 views
11

Używam @functools.lru_cache w Pythonie 3.3. Chciałbym zapisać pamięć podręczną w pliku, aby przywrócić ją po ponownym uruchomieniu programu. Jak mogłem zrobić?Przechowuj pamięć podręczną do pliku functools.lru_cache w Pythonie> = 3.2

Edycja 1 Możliwe rozwiązanie: We need to pickle any sort of callable

Problem trawieniu __closure__:

_pickle.PicklingError: Can't pickle <class 'cell'>: attribute lookup builtins.cell failed 

Gdy próbuję przywrócić funkcję bez niej uzyskać:

TypeError: arg 5 (closure) must be tuple 
+4

Zauważ, że myślę, że realizacja cache LRU zostanie zastąpiony przez realizację C w Pythonie 3.4 lub 3.5, każda próba wydobycia zawartość pamięci podręcznej prawdopodobnie nie będzie przyszłościowy. –

+0

@MartijnPieters: dziękuję za informacje. –

+2

Po prostu unikaj 'lru_cache'. Czy dla twojej funkcji ważne jest posiadanie 'lru_cache' lub prostej pamięci podręcznej? W przeciwnym razie możesz ponownie zaimplementować 'lru_cache' i dodać pożądaną funkcjonalność. – Bakuriu

Odpowiedz

10

Nie możesz zrobić, co chcesz, używając lru_cache, ponieważ nie zapewnia ona interfejsu API do dostępu do pamięci podręcznej, a może być przepisany w C w przyszłych wydaniach. Jeśli naprawdę chcesz zapisać pamięć podręczną, musisz użyć innego rozwiązania, które zapewnia dostęp do pamięci podręcznej.

Wystarczy, że sam napiszesz pamięć podręczną. Na przykład:

from functools import wraps 

def cached(func): 
    @wraps(func) 
    def wrapper(*args): 
     try: 
      return func.cache[args] 
     except KeyError: 
      func.cache[args] = result = func(*args) 
      return result 
    wrapper.cache = {} 
    return wrapper 

Można wtedy zastosować go jako dekorator:

>>> @cached 
... def fibonacci(n): 
...  if n < 2: 
...    return n 
...  return fibonacci(n-1) + fibonacci(n-2) 
... 
>>> fibonacci(100) 
354224848179261915075L 

i pobierać cache:

>>> fibonacci.cache 
{(32,): 2178309, (23,): 28657, ... } 

Można następnie zalewa/unpickle pamięci podręcznej, jak należy i załaduj go:

fibonacci.cache = pickle.load(cache_file_object) 

Znalazłem feature request w trackerze błędów Pythona, aby dodać zrzuty/obciążenia do lru_cache, ale nie zostało to zaakceptowane/zaimplementowane. Być może w przyszłości będzie można mieć wbudowaną obsługę tych operacji za pośrednictwem lru_cache.

+1

Dzięki za kod, spróbuję, myślę, że to może być dobre rozwiązanie. Jestem twórcą żądania funkcji;) Spójrz na datę. –

+0

W zależności od konkretnego przypadku użycia, warto zbudować pamięć podręczną z [półkami] (https://docs.python.org/3.5/library/shelve.html), które są w zasadzie ciągłymi dyktami. –

+0

To właściwie nie działa! Możesz zapisać pamięć podręczną na dysk z piklem - ale ładowanie ich w opisany sposób nie działa. – Nudin

1

Nie jesteś powinien dotykać czegokolwiek w implementacji dekoratora za wyjątkiem publicznego interfejsu API, więc jeśli chcesz zmienić swoje zachowanie, prawdopodobnie musisz skopiować jego implementację i samodzielnie dodać potrzebne funkcje. Pamiętaj, że pamięć podręczna jest obecnie przechowywana jako lista podwójnie połączona, więc zachowaj ostrożność podczas jej zapisywania i ładowania.

+0

Elementy wewnętrzne są dość trudne.Mogłem po prostu edytować je i odsłonić pamięć podręczną, ale wolę, jeśli to możliwe, nie zmieniać domyślnych bibliotek. –

+0

@FrancescoFrassinelli Miałem na myśli, że możesz skopiować implementację do swojego ptoject i zmienić ją. – wRAR

+0

tak, mam to. Nie istnieje sposób na wyeksportowanie funkcji (marszałek?) Lub eksportowanie i importowanie tylko pamięci podręcznej (sprawdź?). –

4

Rozważ użycie trwałego buforowania na dysku przy użyciu joblib.Memory.

Ponieważ dysk jest ogromny, nie ma potrzeby stosowania schematu buforowania LRU.

1

Można użyć biblioteki kopalni, mezmorize

import random 
from mezmorize import Cache 

cache = Cache(CACHE_TYPE='filesystem', CACHE_DIR='cache') 


@cache.memoize() 
def add(a, b): 
    return a + b + random.randrange(0, 1000) 

>>> add(2, 5) 
727 
>>> add(2, 5) 
727 
+1

Działa dobrze, ale nie powinno być "około CACHE_DIR. Niestety, edycja jest możliwa tylko przy co najmniej 6 znakach .... – koalo

Powiązane problemy