Existe uma biblioteca de cache Python?

123

Estou procurando uma biblioteca de cache Python, mas não consigo encontrar nada até agora. Preciso de uma dictinterface simples , onde eu possa definir as chaves e a validade delas e recuperá-las em cache. Tipo de algo como:

cache.get(myfunction, duration=300)

que fornecerá o item do cache, se existir, ou chamará a função e armazenará se não existir ou expirar. Alguém sabe algo assim?

Stavros Korokithakis
fonte
Eu acho que você está faltando itemno seu exemplo.
SilentGhost 15/09/09
Sim, isso provavelmente precisaria de uma chave ... E, 2.x.
Stavros Korokithakis 15/09/09
3
dentro do mesmo processo ou compartilhado entre processos? enfiado ou não?
Aaron Watters
1
Deve ser thread-safe, desculpe, eu deveria ter mencionado. Não preciso compartilhar entre processos.
Stavros Korokithakis 18/09/09
6
Experimente o DiskCache : políticas Apache2 licenciadas, 100% de cobertura, thread-safe, processo-safe, políticas de remoção múltipla e rápidas (benchmarks) .
GrantJ

Respostas:

72

No Python 3.2, você pode usar o decorador @lru_cache da biblioteca functools. É um cache usado pela última vez recentemente, portanto, não há tempo de expiração para os itens, mas como um hack rápido, é muito útil.

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
fonte
20
cachetools oferece uma boa implementação destes e é python compatível 2 e python 3.
vaab
1
grande +1 para cachetools ... parece muito legal e tem algoritmos casal mais cache :)
Jörn Hees
Isso nunca deve ser sugerido! Fique compatível.
PascalVKooten
1
@roboslone, dois anos (menos 4 dias ..) do seu comentário sobre não ser seguro para threads, pode ter sido alterado. Eu tenho cachetools 2.0.0 e vejo no código que ele usa um RLock. /usr/lib/python2.7/site-packages/cachetools/func.py
Motty
@Motty: A documentação para cachetools 4.0.0.0 diz o seguinte: "Lembre-se de que todas essas classes não são seguras para threads . O acesso a um cache compartilhado de vários threads deve ser sincronizado corretamente, por exemplo, usando um dos decoradores de memorização com um objeto de bloqueio adequado "(bold bold)
martineau 03/03
28

Você também pode dar uma olhada no decorador Memoize . Você provavelmente conseguirá fazer o que deseja sem muita modificação.

tgray
fonte
Isso é esperto. Algumas mudanças e o decorador pode até expirar após um tempo definido.
Ehtesh Choudhury
Definitivamente, você pode escrever um limite baseado em espaço no cache no decorador. Isso seria útil se você quisesse uma função, por exemplo, gerar a sequência de fibonacci termo por termo. Você deseja armazenar em cache, mas precisa apenas dos dois últimos valores - salvar todos eles é apenas um espaço ineficiente.
Reem
14

O Joblib https://joblib.readthedocs.io suporta funções de armazenamento em cache no padrão Memoize. Principalmente, a idéia é armazenar em cache funções computacionalmente caras.

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

Você também pode fazer coisas sofisticadas, como usar o decorador @ memory.cache em funções. A documentação está aqui: https://joblib.readthedocs.io/en/latest/generated/joblib.Memory.html

j13r
fonte
2
Como nota de rodapé, o joblib realmente brilha quando você trabalha com matrizes NumPy grandes, pois possui métodos especiais para lidar com elas especificamente.
22414 alexbw
9

Eu acho que a API do python memcached é a ferramenta predominante, mas eu não a usei sozinha e não tenho certeza se ela suporta os recursos necessários.

David Berger
fonte
3
Esse é o padrão da indústria, mas tudo o que eu quero é um mecanismo simples de armazenamento na memória que possa conter cerca de 100 chaves, e o memcached é um pouco exagerado. Obrigado pela resposta, no entanto.
Stavros Korokithakis 15/09/09
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
fonte
5
Fiz algo assim, mas você precisa de bloqueios para multithreading e um parâmetro de tamanho para evitar que ele cresça infinitamente. Então você precisa de alguma função para ordenar as chaves de acessos para descartar os menos acessados, etc etc ...
Stavros Korokithakis
A linha de repr está incorreta (deve usar o self.timeStamp). Além disso, é uma implementação ruim que desnecessariamente faz matemática para cada get (). O tempo de expiração deve ser calculado no CachedItem init.
Ivo
1
De fato, se você estiver implementando apenas o getmétodo, isso não deve ser uma subclasse de ditado, deve ser um objeto com um ditado incorporado.
Ivo
6

Você pode usar minha solução simples para o problema. É realmente simples, nada sofisticado:

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]

Na verdade, falta funcionalidade de expiração, mas você pode estendê-la facilmente especificando uma regra específica no c-tor do MemCache.

O código de esperança é auto-explicativo suficiente, mas, se não, apenas para mencionar, esse cache está sendo transmitido para uma função de tradução como um de seus parâmetros c-tor. Por sua vez, é usado para gerar saída em cache referente à entrada.

Espero que ajude

Jakub Koszuliński
fonte
1
+1 por sugerir algo simples. Dependendo do problema, pode ser apenas a ferramenta para o trabalho. PS Você não precisa o elseem __getitem__:)
hiwaylon
Por que ele não precisa elseno __getitem__? É onde ele preenche o dict ...
Nils Ziehn
5

Tente redis, é uma das soluções mais limpas e fáceis para aplicativos compartilharem dados de maneira atômica ou se você possui alguma plataforma de servidor da web. É muito fácil de configurar, você precisará de um cliente python redis http://pypi.python.org/pypi/redis

atormentar
fonte
1
Deve ser mencionado, está fora de processo, precisa ser acessado usando o TCP.
22418 Jefferson copps
2

Veja gocept.cache no pypi, gerencie o tempo limite.

Andreas Jung
fonte
2

Este projeto tem como objetivo fornecer "Armazenamento em cache para humanos" (parece que é bastante desconhecido)

Algumas informações da página do projeto:

Instalação

cache de instalação do pip

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

keyring é a melhor biblioteca de cache python. Você pode usar

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

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

json_res= keyring.core.delete_password("service","jsonkey")
criança levada
fonte
Essa é uma biblioteca de chaveiros, não uma biblioteca de cache.
Stavros Korokithakis 23/10
@StavrosKorokithakis Na verdade, eu implementei o cache de chaves através keyring
imp