Ziel ist es, eine Scheinklasse zu erstellen, die sich wie eine Datenbank-Ergebnismenge verhält.
Wenn beispielsweise eine Datenbankabfrage mit einem Diktatausdruck zurückgegeben wird {'ab':100, 'cd':200}
, möchte ich Folgendes sehen:
>>> dummy.ab
100
Zuerst dachte ich, ich könnte es vielleicht so machen:
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
def __init__(self, ks, vs):
for i, k in enumerate(ks):
self[k] = vs[i]
setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))
def fn_readonly(self, v)
raise "It is ready only"
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
aber c.ab
Gibt stattdessen ein Eigenschaftsobjekt zurück.
Ersetzen der setattr
Leitung durchk = property(lambda x: vs[i])
ist überhaupt nicht sinnvoll.
Was ist also der richtige Weg, um zur Laufzeit eine Instanzeigenschaft zu erstellen?
PS Mir ist eine Alternative bekannt, die in Wie wird die __getattribute__
Methode verwendet?
python
properties
runtime
monkeypatching
Anthony Kong
quelle
quelle
:
und__init__
Verweiseself.fn_readyonly
.Antworten:
Ich denke, ich sollte diese Antwort erweitern, jetzt wo ich älter und weiser bin und weiß, was los ist. Besser spät als nie.
Sie können einer Klasse dynamisch eine Eigenschaft hinzufügen. Aber das ist der Haken: Sie müssen es der Klasse hinzufügen .
A
property
ist eigentlich eine einfache Implementierung einer Sache, die als Deskriptor bezeichnet wird . Es ist ein Objekt, das eine benutzerdefinierte Behandlung für ein bestimmtes Attribut für eine bestimmte Klasse bietet . Ein bisschen wie ein Weg, einen riesigenif
Baum herauszurechnen__getattribute__
.Als ich frage
foo.b
in dem obigen Beispiel sieht Python , dass dieb
die auf der Klasse implementiert definiert Descriptor Protokoll -die nur bedeutet , es ist ein Objekt mit einem__get__
,__set__
oder__delete__
Verfahren. Der Deskriptor übernimmt die Verantwortung für die Behandlung dieses Attributs, daher ruft Python aufFoo.b.__get__(foo, Foo)
, und der Rückgabewert wird als Wert des Attributs an Sie zurückgegeben. Im Fall vonproperty
ruft jede dieser Methoden nur dasfget
, auffset
, oderfdel
Sie haben es an denproperty
Konstruktor übergeben.Deskriptoren sind wirklich Pythons Methode, um die Installation der gesamten OO-Implementierung aufzudecken. In der Tat gibt es eine andere Art von Deskriptor noch häufiger als
property
.Die bescheidene Methode ist nur eine andere Art von Deskriptor. Seine
__get__
Tacks auf der aufrufenden Instanz als erstes Argument; in der Tat tut es dies:Ich vermute jedenfalls, dass Deskriptoren deshalb nur für Klassen funktionieren: Sie sind eine Formalisierung der Dinge, die Klassen überhaupt antreiben. Sie sind sogar die Ausnahme von der Regel: Sie können einer Klasse offensichtlich Deskriptoren zuweisen, und Klassen sind selbst Instanzen von
type
! In der Tat versucht der Versuch,Foo.b
noch Anrufe zu lesenproperty.__get__
; Es ist nur eine Redewendung für Deskriptoren, sich beim Zugriff als Klassenattribute zurückzugeben.Ich finde es ziemlich cool, dass praktisch das gesamte OO-System von Python in Python ausgedrückt werden kann. :) :)
Oh, und ich habe vor einiger Zeit einen wortreichen Blog-Beitrag über Deskriptoren geschrieben, wenn Sie interessiert sind.
quelle
setattr (Foo, 'name', property (func))
Was Sie also wollen, ist ein Wörterbuch, in dem Sie ein ['b'] als ab buchstabieren können?
Das ist einfach:
quelle
d.items = 1
, gibtd.items
zurück<built-in method items of atdict object at ...>
. Sie könnten noch tund["items"]
oder verwenden__getattribute__
statt__getattr__
, aber dies verhindert , dass die meisten der dict Methoden verwenden.Es scheint, dass Sie dieses Problem viel einfacher mit a lösen können
namedtuple
, da Sie die gesamte Liste der Felder im Voraus kennen.Wenn Sie unbedingt Ihren eigenen Setter schreiben müssen, müssen Sie die Metaprogrammierung auf Klassenebene durchführen.
property()
funktioniert nicht bei Instanzen.quelle
namedtuple
verdient einen Preis dafür, dass er es glatt und elegant macht, treue objektorientierte Prinzipien zu sein.property()
? In beiden Antworten ist nichts spezifisch für schreibgeschützte Eigenschaften.Sie müssen dafür keine Eigenschaft verwenden. Überschreiben Sie
__setattr__
sie einfach , damit sie schreibgeschützt sind.Tada.
quelle
Angenommen, Sie haben ein Objekt, dem Sie eine Eigenschaft hinzufügen möchten. Normalerweise möchte ich Eigenschaften verwenden, wenn ich mit der Verwaltung des Zugriffs auf ein Attribut in Code beginnen möchte, der nachgelagert verwendet wird, damit ich eine konsistente API verwalten kann. Jetzt füge ich sie normalerweise dem Quellcode hinzu, in dem das Objekt definiert ist. Nehmen wir jedoch an, Sie haben diesen Zugriff nicht oder müssen Ihre Funktionen wirklich dynamisch programmgesteuert auswählen.
Erstellen Sie eine Klasse
Lassen Sie uns anhand eines Beispiels, das auf der Dokumentation für
property
basiert, eine Objektklasse mit einem "versteckten" Attribut erstellen und eine Instanz davon erstellen:In Python erwarten wir, dass es einen offensichtlichen Weg gibt, Dinge zu tun. In diesem Fall werde ich jedoch zwei Möglichkeiten zeigen: mit Dekorationsnotation und ohne. Erstens ohne Dekorationsnotation. Dies kann für die dynamische Zuweisung von Gettern, Setzern oder Löschern nützlicher sein.
Dynamisch (auch bekannt als Monkey Patching)
Lassen Sie uns einige für unsere Klasse erstellen:
Und jetzt ordnen wir diese der Eigenschaft zu. Beachten Sie, dass wir unsere Funktionen hier programmgesteuert auswählen und die dynamische Frage beantworten können:
Und Verwendung:
Dekorateure
Wir könnten das gleiche tun wie wir oben mit Dekorateur Notation taten, aber in diesem Fall, wir müssen die Methoden alle die gleichen Namen nennen (und ich würde empfehlen , es genauso aus wie das Attribut zu halten), so programmatische Zuordnung nicht so trivial ist , wie es verwendet die obige Methode:
Und weisen Sie das Eigenschaftsobjekt mit seinen bereitgestellten Setzern und Löschern der Klasse zu:
Und Verwendung:
quelle
Ich habe eine ähnliche Frage zu diesem Stapelüberlauf- Beitrag gestellt , um eine Klassenfactory zu erstellen, die einfache Typen erstellt. Das Ergebnis war diese Antwort, die eine funktionierende Version der Klassenfabrik hatte. Hier ist ein Ausschnitt der Antwort:
Sie können eine Variation davon verwenden, um Standardwerte zu erstellen, die Ihr Ziel sind (es gibt auch eine Antwort in dieser Frage, die sich damit befasst).
quelle
Ich bin mir nicht sicher, ob ich die Frage vollständig verstehe, aber Sie können die Instanzeigenschaften zur Laufzeit mit dem integrierten Element
__dict__
Ihrer Klasse ändern :quelle
self.__dict__[key] = value
Für diejenigen, die von Suchmaschinen kommen, sind hier die beiden Dinge, nach denen ich gesucht habe, wenn ich über dynamische Eigenschaften gesprochen habe:
__dict__
ist gut, wenn Sie dynamisch erstellte Eigenschaften einfügen möchten.__getattr__
Es ist gut, nur dann etwas zu tun, wenn der Wert benötigt wird, z. B. eine Datenbank abzufragen. Die Kombination set / get vereinfacht den Zugriff auf in der Klasse gespeicherte Daten (wie im obigen Beispiel).Wenn Sie nur eine dynamische Eigenschaft möchten, sehen Sie sich die integrierte Funktion property () an.
quelle
Sie können
property()
einer Instanz zur Laufzeit keine neue hinzufügen , da Eigenschaften Datendeskriptoren sind. Stattdessen müssen Sie dynamisch eine neue Klasse erstellen oder überladen,__getattribute__
um Datendeskriptoren für Instanzen zu verarbeiten.quelle
Der beste Weg, dies zu erreichen, ist die Definition
__slots__
. Auf diese Weise können Ihre Instanzen keine neuen Attribute haben.Das druckt
12
Das gibt:
AttributeError: 'C' object has no attribute 'ab'
quelle
Nur ein weiteres Beispiel, wie der gewünschte Effekt erzielt werden kann
Jetzt können wir also Dinge tun wie:
quelle
Hier ist eine Lösung, die:
Nachdem die Klasse definiert wurde, führen Sie dies einfach aus, um ihr dynamisch eine Eigenschaft hinzuzufügen:
Hier ist ein vollständiges Beispiel, das in Python 3 getestet wurde:
quelle
Mit dem folgenden Code können Sie Klassenattribute mithilfe eines Wörterbuchobjekts aktualisieren:
quelle
Das ist ein bisschen anders als das, was OP wollte, aber ich habe mein Gehirn durcheinander gebracht, bis ich eine funktionierende Lösung gefunden habe, also setze ich mich hier für den nächsten Mann / die nächste Frau ein
Ich brauchte eine Möglichkeit, dynamische Setter und Getter anzugeben.
Ich kenne meine Felder im Voraus, also werde ich meine Eigenschaften erstellen. HINWEIS: Sie können diese PER-Instanz nicht ausführen. Diese Eigenschaften sind für die Klasse vorhanden !!!
Lassen Sie uns jetzt alles testen.
Ist es verwirrend? Ja, tut mir leid, ich konnte keine aussagekräftigen Beispiele aus der Praxis finden. Auch dies ist nichts für Unbeschwerte.
quelle
Dies scheint zu funktionieren (siehe aber unten):
Wenn Sie ein komplexeres Verhalten benötigen, können Sie Ihre Antwort jederzeit bearbeiten.
bearbeiten
Folgendes wäre für große Datenmengen wahrscheinlich speichereffizienter:
quelle
Um den Hauptschwerpunkt Ihrer Frage zu beantworten, möchten Sie ein schreibgeschütztes Attribut aus einem Diktat als unveränderliche Datenquelle:
Ich werde zeigen , wie eine verwenden ,
namedtuple
aus demcollections
Modul zu erreichen , nur dies:kehrt zurück
100
quelle
Und die Ausgabe ist:
quelle
Erweiterung der Idee von kjfletch
Ausgabe:
quelle
Obwohl viele Antworten gegeben werden, konnte ich keine finden, mit der ich zufrieden bin. Ich habe meine eigene Lösung gefunden, die
property
Arbeit für den dynamischen Fall macht. Die Quelle zur Beantwortung der ursprünglichen Frage:quelle
Was für mich funktioniert, ist Folgendes:
Ausgabe
quelle
Ich bin kürzlich auf ein ähnliches Problem gestoßen, die Lösung, die ich gefunden habe,
__getattr__
und__setattr__
für die Eigenschaften, die ich behandeln möchte, wird alles andere an die Originale weitergegeben.quelle
Hier ist das einfache Beispiel zum programmgesteuerten Erstellen eines Eigenschaftsobjekts.
quelle
Die einzige Möglichkeit, eine Eigenschaft dynamisch anzuhängen, besteht darin, eine neue Klasse und ihre Instanz mit Ihrer neuen Eigenschaft zu erstellen.
quelle
Viele der bereitgestellten Antworten erfordern so viele Zeilen pro Eigenschaft, dh / und / oder - was ich als hässliche oder langwierige Implementierung betrachten würde, da für mehrere Eigenschaften Wiederholungen erforderlich sind usw. Ich ziehe es vor, die Dinge so lange zu reduzieren / zu vereinfachen, bis sie vorliegen kann nicht mehr vereinfacht werden oder bis es nicht mehr viel Sinn hat, dies zu tun.
Kurz gesagt: Wenn ich in abgeschlossenen Arbeiten zwei Codezeilen wiederhole, konvertiere ich sie normalerweise in eine einzeilige Hilfsfunktion usw. Ich vereinfache mathematische oder ungerade Argumente wie (start_x, start_y, end_x, end_y) in (x, y, w, h) dh x, y, x + w, y + h (manchmal ist min / max erforderlich oder wenn w / h negativ ist und die Implementierung es nicht mag, werde ich von x / subtrahieren y und abs w / h. etc ..).
Das Überschreiben der internen Getter / Setter ist ein guter Weg, aber das Problem ist, dass Sie dies für jede Klasse tun müssen oder die Klasse dieser Basis zuordnen müssen ... Dies funktioniert bei mir nicht so, wie ich es vorziehen würde frei zu wählen die Kinder / Eltern für Vererbung, Kinderknoten, etc.
Ich habe eine Lösung erstellt, die die Frage beantwortet, ohne einen Dict-Datentyp zur Bereitstellung der Daten zu verwenden, da ich die Eingabe der Daten usw. als mühsam empfinde.
Meine Lösung erfordert, dass Sie 2 zusätzliche Zeilen über Ihrer Klasse hinzufügen, um eine Basisklasse für die Klasse zu erstellen, zu der Sie die Eigenschaften hinzufügen möchten. Dann 1 Zeile pro Klasse. Sie haben die Möglichkeit, Rückrufe hinzuzufügen, um die Daten zu steuern und Sie zu informieren, wenn sich Daten ändern , beschränken Sie die Daten, die basierend auf Wert und / oder Datentyp festgelegt werden können, und vieles mehr.
Sie haben auch die Möglichkeit, _object.x, _object.x = value, _object.GetX (), _object.SetX (value) zu verwenden, und diese werden gleich behandelt.
Darüber hinaus sind die Werte die einzigen nicht statischen Daten, die der Klasseninstanz zugewiesen sind, aber die tatsächliche Eigenschaft wird der Klasse zugewiesen, dh die Dinge, die Sie nicht wiederholen möchten, müssen nicht wiederholt werden ... Sie kann einen Standardwert zuweisen, damit der Getter ihn nicht jedes Mal benötigt, obwohl es eine Option zum Überschreiben des Standardstandardwerts gibt, und es gibt eine andere Option, damit der Getter den gespeicherten Rohwert durch Überschreiben der Standardrückgaben zurückgibt (Hinweis: Diese Methode bedeutet, dass der Rohwert nur zugewiesen wird, wenn ein Wert zugewiesen wird, andernfalls ist er None - wenn der Wert Reset ist, weist er None usw. zu.)
Es gibt auch viele Hilfsfunktionen - die erste Eigenschaft, die hinzugefügt wird, fügt der Klasse ungefähr 2 Helfer hinzu, um auf die Instanzwerte zu verweisen ... Es handelt sich um ResetAccessors (_key, ..) -Varargs, die wiederholt werden (alle können mit den zuerst genannten Argumenten wiederholt werden ) und SetAccessors (_key, _value) mit der Option, der Hauptklasse mehr hinzuzufügen, um die Effizienz zu verbessern. Geplant sind: eine Möglichkeit, Accessoren zu gruppieren. Wenn Sie also dazu neigen, jedes Mal einige zurückzusetzen Sie können sie einer Gruppe zuweisen und die Gruppe zurücksetzen, anstatt die genannten Schlüssel jedes Mal zu wiederholen.
Der gespeicherte Instanz- / Rohwert wird in der Klasse gespeichert . , die Klasse. verweist auf die Accessor-Klasse, die statische Variablen / Werte / Funktionen für die Eigenschaft enthält. _Klasse. ist die Eigenschaft selbst, die beim Zugriff über die Instanzklasse beim Setzen / Abrufen usw. aufgerufen wird.
Die Accessor _class .__ zeigt auf die Klasse, aber da sie intern ist, muss sie in der Klasse zugewiesen werden, weshalb ich mich für die Verwendung von __Name = AccessorFunc (...) entschieden habe, eine einzelne Zeile pro Eigenschaft mit vielen optionalen zu verwendende Argumente (unter Verwendung von Keyed Varargs, da sie einfacher und effizienter zu identifizieren und zu warten sind) ...
Wie bereits erwähnt, erstelle ich auch viele Funktionen, von denen einige Accessor-Funktionsinformationen verwenden, sodass sie nicht aufgerufen werden müssen (da dies im Moment etwas unpraktisch ist - im Moment müssen Sie _class verwenden. .FunctionName (_class_instance) verwenden , args) - Ich bin herumgekommen, indem ich den Stack / Trace verwendet habe, um die Instanzreferenz abzurufen, um den Wert zu ermitteln, indem ich die Funktionen hinzugefügt habe, die entweder diesen Bitmarathon ausführen, oder indem ich die Accessoren zum Objekt hinzugefügt und self verwendet habe (benannte dies, um darauf hinzuweisen) sind für die Instanz und um den Zugriff auf sich selbst, die AccessorFunc-Klassenreferenz und andere Informationen aus den Funktionsdefinitionen zu behalten).
Es ist noch nicht ganz fertig, aber es ist ein fantastischer Halt. Hinweis: Wenn Sie zum Erstellen der Eigenschaften nicht __Name = AccessorFunc (...) verwenden, haben Sie keinen Zugriff auf die Taste __, obwohl ich sie in der Init-Funktion definiert habe. Wenn Sie dies tun, gibt es keine Probleme.
Außerdem: Beachten Sie, dass Name und Schlüssel unterschiedlich sind ... Der Name ist 'formal' und wird bei der Erstellung von Funktionsnamen verwendet. Der Schlüssel dient zur Datenspeicherung und zum Zugriff. dh _class.x wobei x in Kleinbuchstaben der Schlüssel ist, wäre der Name Großbuchstabe X, so dass GetX () die Funktion anstelle von Getx () ist, was etwas seltsam aussieht. Dadurch kann self.x funktionieren und angemessen aussehen, aber auch GetX () und angemessen aussehen.
Ich habe eine Beispielklasse eingerichtet, deren Schlüssel / Name identisch und unterschiedlich ist. Viele Hilfsfunktionen wurden erstellt, um die Daten auszugeben (Hinweis: Nicht alles ist vollständig), damit Sie sehen können, was los ist.
Die aktuelle Liste der Funktionen mit key: x, name: X gibt Folgendes aus:
Dies ist keineswegs eine umfassende Liste - es gibt einige, die es zum Zeitpunkt der Veröffentlichung noch nicht geschafft haben ...
Einige der ausgegebenen Daten sind:
Dies ist für eine brandneue Klasse, die mit der Demo-Klasse erstellt wurde, ohne dass andere Daten als der Name zugewiesen wurden (damit sie ausgegeben werden können). Dies ist _foo, der Variablenname, den ich verwendet habe ...
Und dies ist, nachdem alle _foo-Eigenschaften (außer dem Namen) die folgenden Werte in derselben Reihenfolge zugewiesen haben: 'string', 1.0, True, 9, 10, False
Beachten Sie, dass aufgrund eingeschränkter Datentypen oder Werteinschränkungen einige Daten nicht zugewiesen wurden - dies ist beabsichtigt. Der Setter verhindert, dass fehlerhafte Datentypen oder Werte zugewiesen werden, auch wenn sie nicht als Standardwert zugewiesen werden (es sei denn, Sie überschreiben das Standardwertschutzverhalten).
Der Code wurde hier nicht veröffentlicht, weil ich nach den Beispielen und Erklärungen keinen Platz hatte ... Auch weil er sich ändern wird.
Bitte beachten Sie: Zum Zeitpunkt dieses Beitrags ist die Datei unordentlich - dies wird sich ändern. Wenn Sie es jedoch in Sublime Text ausführen und kompilieren oder in Python ausführen, werden eine Menge Informationen kompiliert und ausgespuckt - der AccessorDB-Teil wird nicht ausgeführt (der zum Aktualisieren des Print Getters- und GetKeyOutput-Hilfsprogramms verwendet wird Funktionen zusammen mit der Änderung in eine Instanzfunktion, wahrscheinlich in eine einzelne Funktion eingefügt und umbenannt - suchen Sie danach ..)
Weiter: Es ist nicht alles erforderlich, damit es ausgeführt werden kann. Viele der kommentierten Elemente unten enthalten weitere Informationen zum Debuggen. Möglicherweise ist es beim Herunterladen nicht vorhanden. Wenn dies der Fall ist, sollten Sie in der Lage sein, Kommentare abzugeben und neu zu kompilieren, um weitere Informationen zu erhalten.
Ich suche nach einer Lösung, um MyClassBase zu benötigen: pass, MyClass (MyClassBase): ... - wenn Sie eine Lösung kennen - posten Sie sie.
Das einzige, was in der Klasse benötigt wird, sind die __-Zeilen - der str dient zum Debuggen, ebenso wie der init - sie können aus der Demo-Klasse entfernt werden, aber Sie müssen einige der folgenden Zeilen auskommentieren oder entfernen (_foo / 2/3 ) ..
Die Klassen String, Dict und Util oben sind Teil meiner Python-Bibliothek - sie sind nicht vollständig. Ich habe ein paar Dinge, die ich brauchte, aus der Bibliothek kopiert und ein paar neue erstellt. Der vollständige Code wird mit der gesamten Bibliothek verknüpft und enthält diese zusammen mit der Bereitstellung aktualisierter Aufrufe und dem Entfernen des Codes (der einzige verbleibende Code ist die Demo-Klasse und die Druckanweisungen - das AccessorFunc-System wird in die Bibliothek verschoben). ..
Teil der Datei:
Diese Schönheit macht es unglaublich einfach, neue Klassen mit dynamisch hinzugefügten Eigenschaften mit AccessorFuncs / Rückrufen / Datentyp / Wertdurchsetzung usw. zu erstellen.
Derzeit befindet sich der Link unter (Dieser Link sollte Änderungen am Dokument widerspiegeln.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0
Außerdem: Wenn Sie Sublime Text nicht verwenden, empfehle ich ihn gegenüber Notepad ++, Atom, Visual Code und anderen, da die Verwendung von Threading-Implementierungen die Verwendung erheblich beschleunigt. Ich arbeite auch an einem IDE-ähnlichen Code Mapping-System dafür - werfen Sie einen Blick auf: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Repo zuerst im Paket-Manager hinzufügen, dann Plugin installieren - wenn Version 1.0.0 fertig ist, werde ich hinzufügen es zur Haupt-Plugin-Liste ...)
Ich hoffe diese Lösung hilft ... und wie immer:
Nur weil es funktioniert, macht es nicht richtig - Josh 'Acecool' Moser
quelle