Wie erstelle ich einen begrenzten Memoization Decorator in Python?

8

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.

%Vor%     
agarrett 22.02.2012, 04:56
quelle

3 Antworten

4
%Vor%

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.

%Vor%

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:

%Vor%

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.

    
Ben 22.02.2012, 05:13
quelle
0

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:

%Vor%

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%     
srgerg 22.02.2012 05:21
quelle
-2

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.

    
fileoffset 22.02.2012 05:13
quelle