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?


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)

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.


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


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.


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)

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


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


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


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


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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.