Offensichtlich liefert eine schnelle Suche eine Million Implementierungen und Varianten des Memoization Decorators in Python. Ich bin jedoch an einem Geschmack interessiert, den ich nicht finden konnte. Ich möchte es so haben, dass der Cache von gespeicherten Werten eine feste Kapazität haben kann. Wenn neue Elemente hinzugefügt werden und die Kapazität erreicht ist, wird der älteste Wert entfernt und durch den neuesten Wert ersetzt.
Meine Sorge ist, dass, wenn ich Memoization verwende, um sehr viele Elemente zu speichern, das Programm wegen mangelndem Speicherplatz abstürzt. (Ich weiß nicht, wie gut diese Bedenken in der Praxis liegen mögen.) Wenn der Cache eine feste Größe hätte, wäre ein Speicherfehler kein Problem. Und viele Probleme, an denen ich arbeite, ändern sich, wenn das Programm ausgeführt wird, so dass initial zwischengespeicherte Werte sehr anders aussehen würden als später zwischengespeicherte Werte (und es würde viel weniger wahrscheinlich sein, dass sie später wiederkehren). Deshalb möchte ich, dass die ältesten Sachen durch die neuesten Sachen ersetzt werden.
Ich habe die Klasse OrderedDict
und ein Beispiel gefunden, das zeigt, wie man sie unterteilen kann, um eine maximale Größe anzugeben. Ich möchte das als meinen Cache verwenden, anstatt eine normale dict
. Das Problem ist, ich brauche den Memoize Decorator, um einen Parameter namens maxlen
zu nehmen, der standardmäßig auf None
steht. Wenn es None
ist, dann ist der Cache grenzenlos und arbeitet wie normal. Jeder andere Wert wird als Größe für den Cache verwendet.
Ich möchte, dass es wie folgt funktioniert:
%Vor%und
%Vor%Unten ist der Code, den ich bisher habe, aber ich sehe nicht, wie man den Parameter in den Decorator übergibt, während er sowohl "nackt" als auch mit einem Parameter arbeitet.
%Vor% Bearbeiten : Mit Bens Vorschlag habe ich den folgenden Dekorateur erstellt, von dem ich glaube, dass er so funktioniert, wie ich es mir vorgestellt habe. Es ist mir wichtig, diese dekorierten Funktionen mit multiprocessing
verwenden zu können, und das war in der Vergangenheit ein Problem. Aber ein schneller Test dieses Codes schien korrekt zu funktionieren, selbst wenn die Jobs in einem Pool von Threads bearbeitet wurden.
Hier wird memoize
als eine Funktion verwendet, die für ein einzelnes Funktionsargument aufgerufen wird, und gibt eine Funktion zurück. memoize
ist ein Dekorator.
Hier wird memoize
als eine Funktion verwendet, die für ein einzelnes ganzzahliges Argument aufgerufen wird und eine Funktion zurückgibt, und diese zurückgegebene Funktion wird selbst als Dekorator verwendet, d. h. sie wird für ein einzelnes Funktionsargument aufgerufen und gibt eine Funktion zurück. memoize
ist eine Dekoriererfabrik .
Um diese beiden zu vereinheitlichen, müsst ihr einen hässlichen Code schreiben. Die Art, wie ich es wahrscheinlich tun würde, ist memoize
wie folgt aussehen:
Auf diese Weise können Sie, wenn Sie Parameter übergeben wollen, immer als Schlüsselwortargumente übergeben, wobei func
(was ein Positionsparameter sein sollte) nicht gesetzt ist, und wenn Sie nur wollen, dass alles standardmäßig ist magisch als Dekorateur direkt arbeiten. Das heißt, @memoize(200)
wird Ihnen einen Fehler geben; Sie könnten das vermeiden, indem Sie stattdessen eine Typprüfung durchführen, um zu sehen, ob func
aufrufbar ist, was in der Praxis gut funktionieren sollte, aber nicht wirklich sehr "pythonisch" ist.
Eine Alternative wäre, zwei verschiedene Dekoratoren zu haben, sagen wir memoize
und bounded_memoize
. Das ungebundene memoize
kann eine triviale Implementierung haben, indem einfach bounded_memoize
mit maxlen
auf None
gesetzt wird, also kostet es nichts in der Implementierung oder Wartung.
Normalerweise als Faustregel versuche ich zu vermeiden, eine Funktion zu verfälschen, um zwei nur tangential zusammenhängende Funktionssätze zu implementieren, besonders , wenn sie so unterschiedliche Signaturen haben. Aber in diesem Fall macht es die Verwendung des Dekorators natürlich (erfordert @memoize()
wäre ziemlich fehleranfällig, obwohl es aus einer theoretischen Perspektive konsistenter ist), und Sie werden vermutlich gehen implementieren Sie dies einmal und verwenden Sie es viele Male, so ist die Lesbarkeit am Ort der Verwendung wahrscheinlich das wichtigere Anliegen.
Sie möchten einen Decorator schreiben, der ein Argument akzeptiert (die maximale Länge von BoundedOrderedDict
) und einen Dekorator zurückgibt, der Ihre Funktion mit einem BoundedOrderedDict
der entsprechenden Größe kennzeichnet:
Sie können es wie folgt verwenden:
%Vor%Bearbeiten: Whoops, verpasste einen Teil der Frage. Wenn Sie möchten, dass das Argument maxlen für den Decorator optional ist, können Sie Folgendes tun:
%Vor%Von Ссылка
Die aktuelle Syntax erlaubt Dekoder-Deklarationen auch, eine Funktion aufzurufen, die einen Dekorator zurückgibt:
%Vor%Dies entspricht:
%Vor%Ich bin mir auch nicht sicher, ob ich OrderedDict dafür verwenden würde, ich würde einen Ring Buffer verwenden, sie sind sehr einfach zu implementieren.
Tags und Links python decorator memoization ordereddictionary