Gibt es eine Python-Caching-Bibliothek?


123

Ich suche nach einer Python-Caching-Bibliothek, kann aber bisher nichts finden. Ich brauche eine einfache dictOberfläche, in der ich Schlüssel und deren Ablauf festlegen und sie wieder zwischengespeichert bekommen kann. So etwas wie:

cache.get(myfunction, duration=300)

Dadurch bekomme ich das Element aus dem Cache, falls es vorhanden ist, oder rufe die Funktion auf und speichere es, wenn es nicht vorhanden ist oder abgelaufen ist. Kennt jemand so etwas?


Ich denke, Sie fehlen itemin Ihrem Beispiel.
SilentGhost

Ja, dies würde wahrscheinlich einen Schlüssel benötigen ... Und 2.x.
Stavros Korokithakis

3
innerhalb desselben Prozesses oder zwischen Prozessen geteilt? Gewinde oder nicht?
Aaron Watters

1
Es sollte threadsicher sein, sorry, ich hätte es erwähnen sollen. Ich muss nicht zwischen Prozessen teilen.
Stavros Korokithakis

6
Probieren Sie DiskCache aus : Apache2-lizenziert, 100% Abdeckung, threadsicher, prozesssicher, Richtlinien für die mehrfache Räumung und schnell (Benchmarks) .
GrantJ

Antworten:



72

Ab Python 3.2 können Sie den Decorator @lru_cache aus der functools-Bibliothek verwenden. Es ist ein zuletzt verwendeter Cache, daher gibt es keine Ablaufzeit für die darin enthaltenen Elemente, aber als schneller Hack ist er sehr nützlich.

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 bietet eine schöne Implementierung von diesen und es ist kompatibel mit Python 2 und Python 3.
Vaab

1
big +1 für Cachetools ... scheint ziemlich cool zu sein und hat ein paar weitere Caching-Algorithmen :)
Jörn Hees

Dies sollte niemals vorgeschlagen werden! Bleiben Sie kompatibel.
PascalVKooten

1
@roboslone, zwei Jahre (minus 4 Tage ..) nach Ihrem Kommentar, dass Sie nicht threadsicher sind, hat sich möglicherweise etwas geändert. Ich habe Cachetools 2.0.0 und sehe im Code, dass es ein RLock verwendet. /usr/lib/python2.7/site-packages/cachetools/func.py
Motty

@Motty: In der Dokumentation zu Cachetools 4.0.0.0 heißt es: "Bitte beachten Sie, dass alle diese Klassen nicht threadsicher sind . Der Zugriff auf einen gemeinsam genutzten Cache aus mehreren Threads muss ordnungsgemäß synchronisiert werden, z. B. mithilfe eines der Memoizing-Dekoratoren mit a geeignet Schlossobjekt“(fett Mine)
martineau

28

Sie können sich auch den Memoize-Dekorateur ansehen . Sie könnten es wahrscheinlich dazu bringen, das zu tun, was Sie wollen, ohne zu viel Modifikation.


Das ist schlau. Ein paar Änderungen und der Dekorateur könnte sogar nach einer festgelegten Zeit ablaufen.
Ehtesh Choudhury

Sie könnten definitiv eine platzbasierte Begrenzung für den Cache im Dekorator schreiben. Dies wäre hilfreich, wenn Sie möchten, dass eine Funktion beispielsweise die Fibonacci-Sequenz termweise generiert. Sie möchten das Caching, benötigen jedoch nur die letzten beiden Werte. Das Speichern aller Werte ist nur platzsparend.
Reem

14

Joblib https://joblib.readthedocs.io unterstützt Caching-Funktionen im Memoize-Muster. Meistens besteht die Idee darin, rechenintensive Funktionen zwischenzuspeichern.

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

Sie können auch ausgefallene Dinge tun, wie den @ memory.cache-Dekorator für Funktionen. Die Dokumentation finden Sie hier: https://joblib.readthedocs.io/en/latest/generated/joblib.Memory.html


2
Nebenbei bemerkt, joblib glänzt wirklich, wenn Sie mit großen NumPy-Arrays arbeiten, da es spezielle Methoden gibt, um speziell mit ihnen umzugehen.
Alexbw


9

Ich denke, die Python Memcached API ist das vorherrschende Tool, aber ich habe sie selbst nicht verwendet und bin mir nicht sicher, ob sie die von Ihnen benötigten Funktionen unterstützt.


3
Das ist der Industriestandard, aber alles, was ich will, ist ein einfacher In-Memory-Speichermechanismus, der ungefähr 100 Schlüssel aufnehmen kann, und zwischengespeichert ist ein bisschen übertrieben. Vielen Dank für die Antwort.
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
Ich habe so etwas gemacht, aber Sie benötigen Sperren für Multithreading und einen Größenparameter, um zu verhindern, dass es unendlich wächst. Dann brauchen Sie eine Funktion, um die Schlüssel nach Zugriffen zu sortieren, um die am wenigsten zugegriffenen zu verwerfen, etc etc ...
Stavros Korokithakis

Die Repr- Zeile ist falsch (sollte den self.timeStamp verwenden). Außerdem ist es eine schlechte Implementierung, die unnötig für jedes get () rechnet. Die Ablaufzeit sollte im CachedItem init berechnet werden.
ivo

1
Wenn Sie nur die getMethode implementieren , sollte dies keine Dikt-Unterklasse sein, sondern ein Objekt mit einem eingebetteten Dikt.
ivo

6

Sie können meine einfache Lösung für das Problem verwenden. Es ist wirklich unkompliziert, nichts Besonderes:

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]

Es fehlt zwar die Ablauffunktion, aber Sie können sie leicht erweitern, indem Sie eine bestimmte Regel in MemCache c-tor angeben.

Der Hoffnungscode ist selbsterklärend genug, aber wenn nicht, um nur zu erwähnen, wird dem Cache eine Übersetzungsfunktion als einer seiner C-Tor-Parameter übergeben. Es wird wiederum verwendet, um eine zwischengespeicherte Ausgabe bezüglich der Eingabe zu generieren.

Ich hoffe es hilft


1
+1 für etwas einfaches vorschlagen. Je nach Problem ist es möglicherweise nur das Werkzeug für den Job. PS Sie brauchen das nicht elsein __getitem__:)
hiwaylon

Warum sollte er nicht brauchen , um elsein der __getitem__? Dort bevölkert er das Diktat ...
Nils Ziehn

5

Versuchen Sie es mit redis. Es ist eine der saubersten und einfachsten Lösungen für Anwendungen, um Daten auf atomare Weise gemeinsam zu nutzen, oder wenn Sie über eine Webserverplattform verfügen. Es ist sehr einfach einzurichten, Sie benötigen einen Python Redis Client http://pypi.python.org/pypi/redis


1
Sollte erwähnt werden, Es ist außer Betrieb, muss über TCP zugegriffen werden.
Jeffrey Copps


2

Dieses Projekt zielt darauf ab, "Caching für Menschen" bereitzustellen (scheint jedoch ziemlich unbekannt zu sein)

Einige Infos von der Projektseite:

Installation

Pip Install Cache

Verwendung:

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

Schlüsselring ist die beste Python-Caching-Bibliothek. Sie können verwenden

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

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

json_res= keyring.core.delete_password("service","jsonkey")

Das ist eine Schlüsselbundbibliothek, keine Caching-Bibliothek.
Stavros Korokithakis

@StavrosKorokithakis Eigentlich habe ich das Zwischenspeichern von Schlüsseln durch Schlüsselbund implementiert
imp
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.