Gibt es eine kanonische Möglichkeit, Instanzmethoden in Python zwischenzuspeichern?

9

Ich habe einige rechenintensive Funktionen in meinem Python-Skript, die ich zwischenspeichern möchte. Ich habe nach Lösungen für den Stapelüberlauf gesucht und viele Links gefunden:

  1. /programming/4431703/python-resettable-instance-method-memoization-decorator
  2. https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
  3. http://pythonhosted.org/cachetools/
  4. https://pythonhosted.org/Flask-Cache/ (Ich habe dieses für Kolbenanwendungen verwendet, aber dieses ist keine Kolbenanwendung).

Am Ende habe ich dies in mein Programm eingefügt. Es scheint einfach genug - und funktioniert gut.

class memoized(object):
    '''Decorator. Caches a function's return value each time it is called.
    If called later with the same arguments, the cached value is returned
    (not reevaluated).
    '''
    def __init__(self, func):
        self.func = func
        self.cache = {}

    def __call__(self, *args):
        if not isinstance(args, collections.Hashable):
            return self.func(*args)
        if args in self.cache:
            return self.cache[args]
        else:
            value = self.func(*args)
            self.cache[args] = value
            return value

    def __repr__(self):
        '''Return the function's docstring.'''
        return self.func.__doc__

    def __get__(self, obj, objtype):
        '''Support instance methods.'''
        return functools.partial(self.__call__, obj)

Ich frage mich jedoch, ob es in Python eine kanonische Best Practice gibt. Ich denke, ich habe angenommen, dass es ein sehr häufig verwendetes Paket geben würde, um dies zu handhaben, und bin verwirrt darüber, warum dies nicht existiert. http://pythonhosted.org/cachetools/ ist nur in Version .6 verfügbar und die Syntax ist komplexer als das einfache Hinzufügen eines @ memoize-Dekorators, wie in anderen Lösungen.

bernie2436
quelle

Antworten:

12

Es gibt keinen kanonischen, einzigartig pythonischen Weg, dies zu tun. Jedenfalls ist mir nichts davon bekannt - und ich spreche als jemand, der ausgesehen hat und der Autor eines erfolgreichen Memo-Pakets ist .

Ich glaube jedoch, dass Ihr Mangel an gefundenem Stand der Technik in jeder Hinsicht ein terminologisches Problem sein kann. Sie haben um Caching gebeten . Das ist ein richtiger Begriff, aber er ist zu weit gefasst. Das Zwischenspeichern der Ergebnisse eines bestimmten Funktionsaufrufs oder einer bestimmten Aktivität zur späteren Verwendung wird insbesondere als Memoizing oder Memoization bezeichnet . In der Tat gibt es viele Memo-Pakete von der Community sowie viele Rezepte ( zum Beispiel dieses ). Ich habe auch Memoizng-Funktionen in vielen Mehrzweck-Hilfspaketen gesehen. Viele von ihnen sind ausgereift, kampferprobt und werden ständig in der Produktion verwendet - nicht nur "Code der Version 0.6".

Warum das Auswendiglernen nicht kanonischer oder idiomatischer gehandhabt wird, kann ich nicht sagen. Vielleicht, weil es verschiedene Möglichkeiten gibt, dies mit unterschiedlichen Tugenden und Kompromissen zu erreichen. Oder vielleicht, weil es schon so viele verschiedene Ansätze gibt. Ich finde oft Funktionen - "Liste auflisten" ist eine andere -, die andere Sprachgemeinschaften eifrig zusammenführen, aber die Python-Gemeinschaft oder Kräfte, die es vorziehen, als Rezepte zu behandeln, anstatt sich auf eine bestimmte API festzulegen. In jedem Fall, wenn Ihr Code funktioniert, willkommen in den Reihen der erfolgreichen Memoizer!

Aktualisieren

Da die Python 3-Standardbibliothek (für Version 3.2 und höher) einen lru_cacheDekorator enthält ( Dokumentation hier ), muss ich sagen, dass dies ein bahnbrechender Versuch ist, den häufigsten Anwendungsfall für Memoisierung zu standardisieren. Dass es so spät in Pythons Entwicklung kam, ist wahrscheinlich der Grund, warum es keine gemeinsame Lösung gibt, aber für neuen Code ist das so nah an der Kanonik, wie Sie es finden werden.

Jonathan Eunice
quelle
1
Python 3.8 hat den @cached_propertyDecorator hinzugefügt , der besser für Methoden geeignet ist, deren Rückgabewerte voraussichtlich während der gesamten Lebensdauer der Instanz gleich bleiben.
Patrick Brinich-Langlois
0

Da eine Instanzmethode Selbstattribute verwenden und insbesondere ändern darf, können Sie die Richtigkeit einer beliebigen gespeicherten Instanzmethode nicht garantieren. Darüber hinaus fügt Ihre Implementierung eine implizite Einschränkung für Ihr Objekt hinzu, die hashbar sein soll, und abhängig von diesem Hash ist der Cache-Treffer begrenzt. Sie können nur Klassen verwenden, die Sie verwenden können, und Felder, die Sie Klassen hinzufügen können, die eine gespeicherte Methode haben (oder eine erben).

Wenn Sie sich eine Funktion merken müssen, ist es aus diesen Gründen besser, die teure Instanzmethode in eine statische Methode umzuwandeln und die erforderlichen Objektattribute explizit als Argumente an sie zu übergeben. Dies gibt Ihnen die Freiheit beim Klassendesign und kann den Cache-Treffer verbessern. Dies hilft auch, den Memo-Code zu vereinfachen und zu verallgemeinern. Für dieses Design gibt es feinkörnige Implementierungen, mit denen Sie manchmal die Cache-Größe, die Dauer / die Ungültigmachungsmethoden anpassen können, um die Thread-Sicherheit, die Beständigkeit ...

Arthur Havlicek
quelle