Existe-t-il une bibliothèque de mise en cache Python?

123

Je recherche une bibliothèque de mise en cache Python mais je ne trouve rien pour l'instant. J'ai besoin d'une dictinterface simple où je peux définir les clés et leur expiration et les remettre en cache. Une sorte de quelque chose comme:

cache.get(myfunction, duration=300)

qui me donnera l'élément du cache s'il existe ou appellera la fonction et le stockera s'il n'existe pas ou s'il a expiré. Quelqu'un sait-il quelque chose comme ça?

Stavros Korokithakis
la source
je pense que vous manquez itemdans votre exemple.
SilentGhost
Oui, cela aurait probablement besoin d'une clé ... Et, 2.x.
Stavros Korokithakis
3
dans le même processus ou partagé entre les processus? fileté ou pas?
Aaron Watters le
1
Il devrait être thread-safe, désolé, j'aurais dû le mentionner. Je n'ai pas besoin de partager entre les processus.
Stavros Korokithakis
6
Essayez DiskCache : licence Apache2, couverture à 100%, thread-safe, processus sûr, politiques d'expulsion multiples et rapide (benchmarks) .
GrantJ

Réponses:

52

Jetez un œil à Beaker:

Corbin mars
la source
Ah, j'ai continué à chercher ceci et tout ce que j'ai trouvé était un wiki qui mentionnait comment l'utiliser comme middleware WSGI. Cela ressemble à ce dont j'ai besoin, merci.
Stavros Korokithakis
7
Voir aussi dogpile - soi-disant le nouveau bécher amélioré.
s29
72

Depuis Python 3.2, vous pouvez utiliser le décorateur @lru_cache de la bibliothèque functools. C'est un dernier cache récemment utilisé, il n'y a donc pas de délai d'expiration pour les éléments qu'il contient, mais en tant que piratage rapide, il est très 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)
Genma
la source
20
cachetools offre une belle implémentation de ceux-ci et est compatible python 2 et python 3.
vaab
1
big +1 pour cachetools ... semble plutôt cool et a quelques algorithmes de mise en cache supplémentaires :)
Jörn Hees
Cela ne devrait jamais être suggéré! Restez compatible.
PascalVKooten
1
@roboslone, deux ans (moins 4 jours ..) à partir de votre commentaire sur le fait de ne pas être thread-safe, cela peut avoir changé. J'ai cachetools 2.0.0 et je vois dans le code qu'il utilise un RLock. /usr/lib/python2.7/site-packages/cachetools/func.py
Motty
@Motty: La documentation de cachetools 4.0.0.0 dit ceci: "Veuillez noter que toutes ces classes ne sont pas thread-safe . L'accès à un cache partagé à partir de plusieurs threads doit être correctement synchronisé, par exemple en utilisant l'un des décorateurs de mémorisation avec un objet de verrouillage approprié "(bold mine)
martineau
28

Vous pouvez également jeter un œil au décorateur Memoize . Vous pourriez probablement le faire faire ce que vous voulez sans trop de modifications.

tgray
la source
C'est malin. Quelques changements et le décorateur pourrait même expirer après un temps défini.
Ehtesh Choudhury
Vous pouvez certainement écrire une limite basée sur l'espace pour le cache dans le décorateur. Ce serait utile si vous vouliez qu'une fonction génère, par exemple, la séquence de fibonacci terme par terme. Vous voulez la mise en cache, mais vous n'avez besoin que des deux dernières valeurs - les enregistrer toutes est simplement inefficace en termes d'espace.
reem
14

Joblib https://joblib.readthedocs.io prend en charge les fonctions de mise en cache dans le modèle Memoize. La plupart du temps, l'idée est de mettre en cache les fonctions coûteuses en calcul.

>>> 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)

Vous pouvez également faire des choses fantaisistes comme utiliser le décorateur @ memory.cache sur les fonctions. La documentation est ici: https://joblib.readthedocs.io/en/latest/generated/joblib.Memory.html

j13r
la source
2
En passant, joblib brille vraiment lorsque vous travaillez avec de grands tableaux NumPy, car il dispose de méthodes spéciales pour les traiter spécifiquement.
alexbw
12

Personne n'a encore mentionné les étagères. https://docs.python.org/2/library/shelve.html

Ce n'est pas memcached, mais semble beaucoup plus simple et pourrait répondre à vos besoins.

NucléairePeon
la source
J'ai écrit un wrapper sûr pour les threads et les multiprocessus pour le module standard shelve (y compris une fonction d'assistance pour la mise en cache des requêtes http) au cas où cela serait utile à quiconque: github.com/cristoper/shelfcache
cristoper
9

Je pense que l'API python memcached est l'outil le plus répandu, mais je ne l'ai pas utilisé moi-même et je ne suis pas sûr qu'il prenne en charge les fonctionnalités dont vous avez besoin.

David Berger
la source
3
C'est la norme de l'industrie, mais tout ce que je veux, c'est un simple mécanisme de stockage en mémoire pouvant contenir environ 100 clés, et Memcached est un peu exagéré. Merci pour la réponse.
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)
Tzury Bar Yochay
la source
5
J'ai fait quelque chose comme ça, mais vous avez besoin de verrous pour le multithreading et d'un paramètre de taille pour éviter qu'il ne grandisse à l'infini. Ensuite, vous avez besoin d'une fonction pour trier les clés par accès afin d'éliminer les moins consultées, etc etc ...
Stavros Korokithakis
La ligne repr est incorrecte (doit utiliser self.timeStamp). De plus, c'est une mauvaise implémentation qui fait inutilement des calculs pour chaque get (). Le délai d'expiration doit être calculé dans l'initialisation CachedItem.
ivo
1
En fait, si vous implémentez uniquement la getméthode, cela ne devrait pas être une sous-classe de dict, cela devrait être un objet avec un dict intégré.
ivo
6

Vous pouvez utiliser ma solution simple au problème. C'est vraiment simple, rien d'extraordinaire:

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]

Il manque en effet de fonctionnalité d'expiration, mais vous pouvez facilement l'étendre en spécifiant une règle particulière dans MemCache c-tor.

J'espère que le code est suffisamment explicite, mais si ce n'est pas le cas, il suffit de mentionner que le cache reçoit une fonction de traduction comme l'un de ses paramètres c-tor. Il est utilisé à son tour pour générer une sortie mise en cache concernant l'entrée.

J'espère que ça aide

Jakub Koszuliński
la source
1
+1 pour avoir suggéré quelque chose de simple. En fonction du problème, il peut s'agir simplement de l'outil pour le travail. PS Vous n'avez pas besoin du elsein __getitem__:)
hiwaylon
Pourquoi n'en aurait-il pas besoin elsedans le __getitem__? C'est là qu'il remplit le dict ...
Nils Ziehn
5

Essayez redis, c'est l'une des solutions les plus propres et les plus simples pour les applications pour partager des données de manière atomique ou si vous avez une plate-forme de serveur Web. C'est très facile à installer, vous aurez besoin d'un client python redis http://pypi.python.org/pypi/redis

harry
la source
1
Doit être mentionné, il est hors processus, doit être accessible en utilisant TCP.
jeffry copps
2

Regardez gocept.cache sur pypi, gérez le délai d'expiration.

Andreas Jung
la source
2

Ce projet vise à fournir une "mise en cache pour les humains" (cela semble assez inconnu cependant)

Quelques informations de la page du projet:

Installation

cache d'installation de pip

Usage:

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()
Vano
la source
-5

keyring est la meilleure bibliothèque de mise en cache python. Vous pouvez utiliser

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

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

json_res= keyring.core.delete_password("service","jsonkey")
lutin
la source
C'est une bibliothèque de trousseaux de clés, pas une bibliothèque de mise en cache.
Stavros Korokithakis
@StavrosKorokithakis En fait, j'ai implémenté la mise en cache des clés via le trousseau de clés
imp