2013-03-23 8 views
11

Sto usando @functools.lru_cache in Python 3.3. Vorrei salvare la cache in un file, per ripristinarlo quando il programma verrà riavviato. Come potrei fare?Memorizza la cache in un file functools.lru_cache in Python> = 3.2

Modifica 1 Possibile soluzione: We need to pickle any sort of callable

Problema decapaggio __closure__:

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

Se provo a ripristinare la funzione senza di esso, ottengo:

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

Nota che penso che l'attuazione della cache LRU sta per essere sostituita da un'implementazione C in Python 3.4 o 3.5, qualsiasi tentativo di estrarre il contenuto della cache è probabilmente non sarà a prova di futuro. –

+0

@MartijnPieters: grazie per l'informazione. –

+2

Evita semplicemente 'lru_cache'. È importante che la tua funzione abbia un 'lru_cache' o che sia sufficiente una semplice cache? Altrimenti puoi reimplementare 'lru_cache' e aggiungere la funzionalità desiderata. – Bakuriu

risposta

10

Non è possibile eseguire ciò che si desidera utilizzando lru_cache, poiché non fornisce un'API per accedere alla cache e potrebbe essere riscritta in C nelle versioni future. Se vuoi veramente salvare la cache devi usare una soluzione diversa che ti dà accesso alla cache.

È abbastanza semplice scrivere una cache da soli. Per esempio:

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 

si può quindi applicare come decoratore:

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

e recuperare il cache:

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

È quindi possibile salamoia/deserializzazione cache come ti pare e caricarlo con:

fibonacci.cache = pickle.load(cache_file_object) 

Ho trovato un feature request nel tracker dei problemi di Python per aggiungere dump/carichi a lru_cache, ma non è stato accettato/implementato. Forse in futuro sarà possibile avere il supporto integrato per queste operazioni tramite lru_cache.

+1

Grazie per il codice, ci proverò, penso che potrebbe essere una buona soluzione. Sono il creatore della richiesta di funzionalità;) Guarda la data. –

+0

A seconda del caso d'uso esatto, potrebbe valere la pena di costruire la cache con [shelf] (https://docs.python.org/3.5/library/shelve.html), che sono in pratica dts persistenti. –

+0

Questo in realtà non funziona! Puoi salvare la cache su disco con pickle con questo - ma il caricamento come dichiarato non funziona. – Nudin

1

Non sei dovrebbe toccare qualsiasi cosa all'interno dell'implementazione del decoratore tranne che per l'API pubblica, quindi se tu vuoi cambiare il suo comportamento probabilmente hai bisogno di copiare la sua implementazione e aggiungere le funzioni necessarie tu stesso. Si noti che la cache è attualmente memorizzata come lista circolare doppiamente collegata, quindi è necessario fare attenzione quando si salva e si carica.

+0

Gli interni sono abbastanza difficili.Potrei semplicemente modificarli ed esporre la cache, ma preferisco, se possibile, di non modificare le librerie predefinite. –

+0

@FrancescoFrassinelli volevo dire che puoi copiare l'implementazione nel tuo ptoject e cambiarla. – wRAR

+0

sì, ho capito. Non esiste un modo per esportare la funzione (marshal?) O esportare e importare solo la cache (inspect?). –

4

Considerare l'utilizzo di joblib.Memory per la cache persistente sul disco.

Poiché il disco è enorme, non è necessario uno schema di cache LRU.

1

È possibile utilizzare una libreria di mio, 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

Funziona bene, ma non dovrebbe esserci intorno a CACHE_DIR. Sfortunatamente, una modifica è possibile solo con almeno 6 caratteri .... – koalo