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?


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)

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.


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


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.


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)

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


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


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


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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.