Kürzlich habe ich eine vorhandene Codebasis durchlaufen, die viele Klassen enthält, in denen Instanzattribute in einer Datenbank gespeicherte Werte widerspiegeln. Ich habe viele dieser Attribute überarbeitet, damit ihre Datenbanksuchen verschoben werden, d. H. nicht im Konstruktor initialisiert werden, sondern erst beim ersten Lesen. Diese Attribute ändern sich nicht über die Lebensdauer der Instanz, stellen jedoch einen echten Engpass bei der Berechnung des ersten Males dar und werden nur in besonderen Fällen wirklich aufgerufen. Daher können sie auch zwischengespeichert werden, nachdem sie aus der Datenbank abgerufen wurden (dies entspricht daher der Definition der Memoisierung, bei der die Eingabe einfach "keine Eingabe" ist).
Ich tippe immer wieder den folgenden Codeausschnitt für verschiedene Attribute in verschiedenen Klassen ein:
class testA(object):
def __init__(self):
self._a = None
self._b = None
@property
def a(self):
if self._a is None:
# Calculate the attribute now
self._a = 7
return self._a
@property
def b(self):
#etc
Gibt es in Python bereits einen Dekorateur, der dies einfach nicht kennt? Oder gibt es eine einigermaßen einfache Möglichkeit, einen Dekorateur zu definieren, der dies tut?
Ich arbeite unter Python 2.5, aber 2.6-Antworten könnten immer noch interessant sein, wenn sie sich erheblich unterscheiden.
Hinweis
Diese Frage wurde gestellt, bevor Python viele fertige Dekorateure dafür einbezog. Ich habe es nur aktualisiert, um die Terminologie zu korrigieren.
functools.lru_cache()
.Antworten:
Für alle Arten von großartigen Dienstprogrammen verwende ich Boltons .
Als Teil dieser Bibliothek haben Sie die Eigenschaft zwischengespeichert :
quelle
Hier ist eine Beispielimplementierung eines faulen Immobiliendekorateurs:
Interaktive Sitzung:
quelle
__get__
@wraps(fn)
@property
wraps
functools
Ich habe dieses für mich selbst geschrieben ... Für echte einmal berechnete faule Eigenschaften. Ich mag es, weil es vermeidet, zusätzliche Attribute auf Objekte zu kleben, und wenn es einmal aktiviert ist, verschwendet es keine Zeit damit, nach Attributpräsenz zu suchen usw.:
Hinweis: Die
lazy_property
Klasse ist ein Nicht-Daten-Deskriptor , dh sie ist schreibgeschützt. Das Hinzufügen einer__set__
Methode würde verhindern, dass sie ordnungsgemäß funktioniert.quelle
fget
Weg@property
. Um Unveränderlichkeit / Idempotenz sicherzustellen, müssen Sie eine__set__()
Methode hinzufügen , die ausgelöst wirdAttributeError('can\'t set attribute')
(oder welche Ausnahme / Nachricht auch immer zu Ihnen passt, aber dies ist es, wasproperty
ausgelöst wird). Dies kommt leider mit einer Auswirkung auf die Leistung von einem Bruchteil einer Mikrosekunde , da__get__()
wird bei jedem Zugriff aufgerufen werden , anstatt ziehen fget Wert von dict auf den zweiten und den späteren Zugriff. Es lohnt sich meiner Meinung nach, die Unveränderlichkeit / Idempotenz aufrechtzuerhalten, was für meine Anwendungsfälle von entscheidender Bedeutung ist, aber YMMV.Hier ist eine aufrufbare , die ein optionales Timeout - Argument, in der
__call__
Sie auch die Kopie über konnten__name__
,__doc__
,__module__
von func Namespace:Ex:
quelle
property
ist eine Klasse. Ein Deskriptor um genau zu sein. Leiten Sie einfach daraus ab und implementieren Sie das gewünschte Verhalten.quelle
Was Sie wirklich wollen, ist der
reify
(Quelle verknüpft!) Dekorateur von Pyramid:quelle
:)
pyramid
Abhängigkeit erforderlich ist.Es gibt eine Verwechslung von Begriffen und / oder Verwechslungen von Konzepten sowohl in Frage als auch in Antworten.
Eine verzögerte Auswertung bedeutet nur, dass etwas zur Laufzeit zum letztmöglichen Zeitpunkt ausgewertet wird, wenn ein Wert benötigt wird.
Der Standarddekorateur(*) Die dekorierte Funktion wird nur ausgewertet und jedes Mal, wenn Sie den Wert dieser Eigenschaft benötigen. (siehe Wikipedia-Artikel über faule Bewertung)@property
macht genau das.(*) Tatsächlich ist eine echte faule Bewertung (vergleiche z. B. Haskell) in Python sehr schwer zu erreichen (und führt zu Code, der alles andere als idiomatisch ist).
Auswendiglernen ist der richtige Begriff für das, wonach der Fragesteller zu suchen scheint. Reine Funktionen , die auf Nebenwirkungen für Rückgabewert Auswertung hängen nicht sicher memoized werden können , und es ist eigentlich ein Dekorateur in functools
@functools.lru_cache
so dass keine Notwendigkeit für das Schreiben von eigenen Dekorateure , wenn Sie Verhalten spezialisiert müssen.quelle
@property
"faul" zu diesem Zeitpunkt wenig Sinn macht , da meine Frage bereits eine Lösung mit " voraussetzt ". (Ich dachte auch an Memoisation als eine Karte von Eingaben zu zwischengespeicherten Ausgaben, und da diese Eigenschaften nur eine Eingabe haben, nichts, schien eine Karte komplexer als nötig zu sein.)Sie können dies schön und einfach tun, indem Sie eine Klasse aus der nativen Python-Eigenschaft erstellen:
Wir können diese Eigenschaftsklasse wie eine reguläre Klasseneigenschaft verwenden (sie unterstützt auch die Elementzuweisung, wie Sie sehen können).
Der Wert wurde nur beim ersten Mal berechnet und danach haben wir unseren gespeicherten Wert verwendet
Ausgabe:
quelle