Czy istnieje biblioteka buforująca Python?

123

Szukam biblioteki buforującej Python, ale na razie nic nie mogę znaleźć. Potrzebuję prostego dictinterfejsu, w którym mogę ustawić klucze i ich wygaśnięcie oraz odzyskać je w pamięci podręcznej. Coś takiego:

cache.get(myfunction, duration=300)

co da mi element z pamięci podręcznej, jeśli istnieje, lub wywoła funkcję i zapisze ją, jeśli nie ma lub wygasła. Czy ktoś coś takiego wie?

Stavros Korokithakis
źródło
myślę, że brakuje ci itemw twoim przykładzie.
SilentGhost
Tak, to prawdopodobnie wymagałoby klucza ... I, 2.x.
Stavros Korokithakis
3
w ramach tego samego procesu lub współdzielone między procesami? gwintowane czy nie?
Aaron Watters
1
Powinien być bezpieczny dla wątków, przepraszam, powinienem był wspomnieć. Nie muszę udostępniać plików między procesami.
Stavros Korokithakis
6
Wypróbuj DiskCache : licencja Apache2, 100% pokrycie, bezpieczeństwo wątków, bezpieczeństwo procesów, wiele zasad eksmisji i szybkość (testy porównawcze) .
GrantJ

Odpowiedzi:

72

Od Pythona 3.2 możesz użyć dekoratora @lru_cache z biblioteki functools. Jest to ostatnio używana pamięć podręczna, więc nie ma czasu wygaśnięcia dla zawartych w niej przedmiotów, ale jako szybki hack jest bardzo przydatna.

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
źródło
20
cachetools oferuje przyjemną implementację tych i jest kompatybilny z
Pythonem
1
duże +1 dla cachetools ... wydaje się całkiem fajne i ma jeszcze kilka algorytmów buforowania :)
Jörn Hees
To nigdy nie powinno być sugerowane! Zachowaj zgodność.
PascalVKooten
1
@roboslone, dwa lata (minus 4 dni ..) od twojego komentarza o braku bezpieczeństwa wątków, mogło się to zmienić. Mam cachetools 2.0.0 i widzę w kodzie, że używa RLock. /usr/lib/python2.7/site-packages/cachetools/func.py
Motty
@Motty: Dokumentacja dla cachetools 4.0.0.0 mówi tak: „Należy pamiętać, że wszystkie te klasy nie są bezpieczne dla wątków . Dostęp do współdzielonej pamięci podręcznej z wielu wątków musi być odpowiednio zsynchronizowany, np. Za pomocą jednego z dekoratorów zapamiętujących z elementem odpowiedni obiekt zamka ”(moje pogrubienie)
martineau
28

Możesz również rzucić okiem na dekorator Memoize . Prawdopodobnie mógłbyś sprawić, by robił to, co chcesz, bez zbytniej modyfikacji.

tgray
źródło
To sprytne. Kilka zmian i dekorator może nawet wygasnąć po określonym czasie.
Ehtesh Choudhury
Zdecydowanie możesz napisać ograniczenie oparte na spacji do pamięci podręcznej w dekoratorze. Byłoby to pomocne, gdybyś chciał, aby funkcja na przykład generowała termin sekwencji Fibonacciego według terminu. Chcesz buforować, ale potrzebujesz tylko dwóch ostatnich wartości - oszczędzanie ich wszystkich jest po prostu nieefektywne pod względem miejsca.
powtórz
14

Joblib https://joblib.readthedocs.io obsługuje funkcje buforowania we wzorcu Memoize. Głównie chodzi o buforowanie kosztownych obliczeniowo funkcji.

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

Możesz także robić wymyślne rzeczy, takie jak dekorator @ memory.cache dla funkcji. Dokumentacja jest tutaj: https://joblib.readthedocs.io/en/latest/generated/joblib.Memory.html

j13r
źródło
2
Na marginesie, joblib naprawdę błyszczy, gdy pracujesz z dużymi tablicami NumPy, ponieważ ma specjalne metody radzenia sobie z nimi.
alexbw
9

Myślę, że Python memcached API jest najpopularniejszym narzędziem, ale sam go nie używałem i nie jestem pewien, czy obsługuje funkcje, których potrzebujesz.

David Berger
źródło
3
To jest standard branżowy, ale wszystko, czego chcę, to prosty mechanizm przechowywania w pamięci, który może pomieścić około 100 kluczy, a memcached to trochę przesada. Dziękuję jednak za odpowiedź.
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
źródło
5
Zrobiłem coś takiego, ale potrzebujesz blokad do wielowątkowości i parametru rozmiaru, aby uniknąć nieskończonego wzrostu. W takim razie potrzebujesz jakiejś funkcji do sortowania kluczy według dostępu, aby pozbyć się tych, które mają najmniejszy dostęp, itp. Itd.
Stavros Korokithakis
Repr linia jest niepoprawna (należy użyć self.timeStamp). Jest to również kiepska implementacja, która niepotrzebnie wykonuje obliczenia matematyczne dla każdego polecenia get (). Czas wygaśnięcia należy obliczyć w inicjalizacji CachedItem.
ivo
1
W rzeczywistości, jeśli implementujesz tylko getmetodę, nie powinna to być podklasa dict, powinien to być obiekt z osadzoną dyktą.
ivo
6

Możesz skorzystać z mojego prostego rozwiązania problemu. To naprawdę proste, nic nadzwyczajnego:

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]

W istocie brakuje mu funkcji wygasania, ale można ją łatwo rozszerzyć, określając konkretną regułę w c-torze MemCache.

Mam nadzieję, że kod jest wystarczająco oczywisty, ale jeśli nie, wystarczy wspomnieć, że pamięć podręczna jest przekazywana jako jeden z parametrów c-tor jako funkcja tłumaczenia. Służy z kolei do generowania buforowanych danych wyjściowych dotyczących danych wejściowych.

Mam nadzieję, że to pomoże

Jakub Koszuliński
źródło
1
+1 za zasugerowanie czegoś prostego. W zależności od problemu może to być po prostu narzędzie do pracy. PS Nie potrzebujesz elsew __getitem__:)
hiwaylon
Dlaczego nie miałby tego robić elsew __getitem__? Tam wypełnia dyktando ...
Nils Ziehn
5

Wypróbuj redis, jest to jedno z najczystszych i najłatwiejszych rozwiązań dla aplikacji do udostępniania danych w sposób atomowy lub jeśli masz platformę serwera WWW. Jest bardzo łatwy w konfiguracji, potrzebujesz klienta Python redis http://pypi.python.org/pypi/redis

złupić
źródło
1
Należy wspomnieć, że jest poza procesem, musi być dostępny za pomocą protokołu TCP.
Jeffry Copps
2

Ten projekt ma na celu zapewnienie „buforowania dla ludzi” (wydaje się jednak, że jest to dość nieznane)

Kilka informacji ze strony projektu:

Instalacja

pamięć podręczna instalacji pip

Stosowanie:

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
źródło
-5

Keyring to najlepsza biblioteka buforująca Pythona. Możesz użyć

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

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

json_res= keyring.core.delete_password("service","jsonkey")
chochlik
źródło
To jest biblioteka kluczy, a nie biblioteka pamięci podręcznej.
Stavros Korokithakis
@StavrosKorokithakis Właściwie zaimplementowałem buforowanie kluczy za pomocą breloka
imp