Esiste una libreria di cache Python?


123

Sto cercando una libreria di memorizzazione nella cache Python ma non riesco a trovare nulla finora. Ho bisogno di un'interfaccia semplice in dictcui posso impostare le chiavi e la loro scadenza e ripristinarle nella cache. Una specie di qualcosa come:

cache.get(myfunction, duration=300)

che mi darà l'elemento dalla cache se esiste o chiamerà la funzione e lo memorizzerà se non lo è o è scaduto. Qualcuno sa qualcosa di simile?


penso che ti manchi itemnel tuo esempio.
SilentGhost

Sì, questo probabilmente avrebbe bisogno di una chiave ... E, 2.x.
Stavros Korokithakis

3
all'interno dello stesso processo o condiviso tra processi? filettato o no?
Aaron Watters

1
Dovrebbe essere thread-safe, mi dispiace, avrei dovuto menzionarlo. Non ho bisogno di condividere tra i processi.
Stavros Korokithakis

6
Prova DiskCache : licenza Apache2, copertura al 100%, thread-safe, process-safe, criteri di eliminazione multipli e veloci (benchmark) .
GrantJ

Risposte:



72

Da Python 3.2 puoi usare decorator @lru_cache dalla libreria functools. È una cache utilizzata per ultimo di recente, quindi non c'è tempo di scadenza per gli elementi in essa contenuti, ma come hack veloce è molto utile.

from functools import lru_cache

@lru_cache(maxsize=256)
def f(x):
  return x*x

for x in range(20):
  print f(x)
for x in range(20):
  print f(x)

20
cachetools offre una bella implementazione di questi ed è compatibile con python 2 e python 3.
vaab

1
grande +1 per cachetools ... sembra piuttosto interessante e ha un paio di algoritmi di cache in più :)
Jörn Hees

Questo non dovrebbe mai essere suggerito! Rimani compatibile.
PascalVKooten

1
@roboslone, a due anni (meno 4 giorni ..) dal tuo commento sul non essere thread-safe, potrebbe essere cambiato. Ho cachetools 2.0.0 e vedo nel codice che utilizza un RLock. /usr/lib/python2.7/site-packages/cachetools/func.py
Motty

@ Motty: La documentazione per cachetools 4.0.0.0 dice questo: "Tieni presente che tutte queste classi non sono thread-safe . L'accesso a una cache condivisa da più thread deve essere sincronizzato correttamente, ad esempio utilizzando uno dei decoratori di memo con un oggetto di blocco adatto "(grassetto mio)
martineau

28

Potresti anche dare un'occhiata al decoratore Memoize . Probabilmente potresti farlo fare quello che vuoi senza troppe modifiche.


È intelligente. Poche modifiche e il decoratore potrebbe anche scadere dopo un tempo prestabilito.
Ehtesh Choudhury

Potresti sicuramente scrivere un limite basato sullo spazio per la cache nel decoratore. Sarebbe utile se si volesse, ad esempio, che una funzione generi la sequenza di Fibonacci termine per termine. Vuoi la cache, ma hai solo bisogno degli ultimi due valori: salvarli tutti è solo inefficiente di spazio.
reem

14

Joblib https://joblib.readthedocs.io supporta le funzioni di caching nel pattern Memoize. L'idea è principalmente quella di memorizzare nella cache funzioni costose dal punto di vista computazionale.

>>> from joblib import Memory
>>> mem = Memory(cachedir='/tmp/joblib')
>>> import numpy as np
>>> square = mem.cache(np.square)
>>> 
>>> a = np.vander(np.arange(3)).astype(np.float)
>>> b = square(a)                                   
________________________________________________________________________________
[Memory] Calling square...
square(array([[ 0.,  0.,  1.],
       [ 1.,  1.,  1.],
       [ 4.,  2.,  1.]]))
___________________________________________________________square - 0...s, 0.0min

>>> c = square(a)

Puoi anche fare cose fantasiose come usare il decoratore @ memory.cache sulle funzioni. La documentazione è qui: https://joblib.readthedocs.io/en/latest/generated/joblib.Memory.html


2
Come nota a margine, joblib brilla davvero quando lavori con grandi array NumPy, dal momento che ha metodi speciali per gestirli in modo specifico.
alexbw


9

Penso che l'API memcached di python sia lo strumento prevalente, ma non l'ho usato da solo e non sono sicuro che supporti le funzionalità di cui hai bisogno.


3
Questo è lo standard del settore, ma tutto ciò che voglio è un semplice meccanismo di archiviazione in memoria che possa contenere circa 100 chiavi, e memcached è un po 'eccessivo. Grazie per la risposta, però.
Stavros Korokithakis

7
import time

class CachedItem(object):
    def __init__(self, key, value, duration=60):
        self.key = key
        self.value = value
        self.duration = duration
        self.timeStamp = time.time()

    def __repr__(self):
        return '<CachedItem {%s:%s} expires at: %s>' % (self.key, self.value, time.time() + self.duration)

class CachedDict(dict):

    def get(self, key, fn, duration):
        if key not in self \
            or self[key].timeStamp + self[key].duration < time.time():
                print 'adding new value'
                o = fn(key)
                self[key] = CachedItem(key, o, duration)
        else:
            print 'loading from cache'

        return self[key].value



if __name__ == '__main__':

    fn = lambda key: 'value of %s  is None' % key

    ci = CachedItem('a', 12)
    print ci 
    cd = CachedDict()
    print cd.get('a', fn, 5)
    time.sleep(2)
    print cd.get('a', fn, 6)
    print cd.get('b', fn, 6)
    time.sleep(2)
    print cd.get('a', fn, 7)
    print cd.get('b', fn, 7)

5
Ho fatto qualcosa del genere, ma hai bisogno di blocchi per il multithreading e un parametro di dimensione per evitare che cresca all'infinito. Quindi hai bisogno di una funzione per ordinare le chiavi in ​​base agli accessi per scartare quelle meno accessibili, ecc ecc ...
Stavros Korokithakis

La repr linea è corretta (dovrebbe utilizzare il self.timeStamp). Inoltre è una cattiva implementazione che fa inutilmente matematica per ogni get (). Il tempo di scadenza deve essere calcolato nell'init CachedItem.
ivo

1
In effetti, se stai solo implementando il getmetodo, questa non dovrebbe essere una sottoclasse dict, dovrebbe essere un oggetto con un dict incorporato.
ivo

6

Puoi usare la mia semplice soluzione al problema. È davvero semplice, niente di speciale:

class MemCache(dict):
    def __init__(self, fn):
        dict.__init__(self)
        self.__fn = fn

    def __getitem__(self, item):
        if item not in self:
            dict.__setitem__(self, item, self.__fn(item))
        return dict.__getitem__(self, item)

mc = MemCache(lambda x: x*x)

for x in xrange(10):
    print mc[x]

for x in xrange(10):
    print mc[x]

In effetti manca la funzionalità di scadenza, ma puoi facilmente estenderla specificando una regola particolare in MemCache c-tor.

Il codice Hope è abbastanza autoesplicativo, ma in caso contrario, solo per menzionare, che alla cache viene passata una funzione di traduzione come uno dei suoi parametri c-tor. Viene utilizzato a sua volta per generare l'output memorizzato nella cache relativo all'input.

Spero che sia d'aiuto


1
+1 per aver suggerito qualcosa di semplice. A seconda del problema, potrebbe essere solo lo strumento per il lavoro. PS Non hai bisogno elsedi __getitem__:)
hiwaylon

Perché avrebbe dovuto: non è necessario elsenel __getitem__? È lì che popola il dict ...
Nils Ziehn

5

Prova redis, è una delle soluzioni più pulite e semplici per le applicazioni per condividere i dati in modo atomico o se hai qualche piattaforma di server web. È molto facile da configurare, avrai bisogno di un client Python Redis http://pypi.python.org/pypi/redis


1
Dovrebbe essere menzionato, è fuori processo, è necessario accedervi utilizzando TCP.
jeffry copps


2

Questo progetto mira a fornire "Caching per gli esseri umani" (sembra che sia abbastanza sconosciuto però)

Alcune info dalla pagina del progetto:

Installazione

pip install cache

Uso:

import pylibmc
from cache import Cache

backend = pylibmc.Client(["127.0.0.1"])

cache = Cache(backend)

@cache("mykey")
def some_expensive_method():
    sleep(10)
    return 42

# writes 42 to the cache
some_expensive_method()

# reads 42 from the cache
some_expensive_method()

# re-calculates and writes 42 to the cache
some_expensive_method.refresh()

# get the cached value or throw an error
# (unless default= was passed to @cache(...))
some_expensive_method.cached()


-5

keyring è la migliore libreria di cache di Python. Puoi usare

keyring.set_password("service","jsonkey",json_res)

json_res= keyring.get_password("service","jsonkey")

json_res= keyring.core.delete_password("service","jsonkey")

Questa è una libreria di portachiavi, non una libreria di cache.
Stavros Korokithakis

@StavrosKorokithakis In realtà, ho implementato la memorizzazione nella cache delle chiavi tramite portachiavi
imp
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.