Python rekursive setattr () - ähnliche Funktion zum Arbeiten mit verschachtelten Wörterbüchern

8

Es gibt viele gute getattr () - ähnliche Funktionen zum Parsen verschachtelter Dictionary-Strukturen, wie zum Beispiel:

Ich möchte eine parallele setattr () machen. Im Wesentlichen gegeben:

%Vor%

Ich möchte eine Funktion erzeugen, die ich zuweisen kann:

%Vor%

Dies funktioniert mehr oder weniger genauso wie:

%Vor%

ergibt:

%Vor%

Ich nenne es gerade setByDot() :

%Vor%

Ein Problem damit ist, dass, wenn ein Schlüssel in der Mitte nicht existiert, Sie nach einem Zwischenschlüssel suchen müssen und einen Zwischenschlüssel erstellen müssen, wenn er nicht existiert - dh für das obige:

%Vor%

Also müssen Sie zuerst Folgendes machen:

%Vor%

Ein anderes ist, dass die Eingabe für das nächste Element eine Liste ist, die sich von der Eingabe unterscheidet, wenn das nächste Element eine Zeichenfolge ist, dh:

%Vor%

... schlägt fehl, weil die Zuweisung für eine Null-Zeichenfolge anstelle eines Null-Dikts erfolgte. Das Null-Diktat ist die richtige Zuweisung für jede Nicht-Liste in Diktat bis zum letzten - das kann eine Liste oder ein Wert sein.

Ein zweites Problem, auf das in den Kommentaren von @TokenMacGuy hingewiesen wird, ist, dass Sie, wenn Sie eine Liste erstellen müssen, die nicht existiert, sehr viele leere Werte erstellen müssen. Also,

%Vor%

--- kann bedeuten, dass der Algorithmus ein Zwischenkommando wie:

machen muss %Vor%

ergibt

%Vor%

so, dass dies der Setter ist, der dem Getter zugeordnet ist ...

%Vor%

Noch wichtiger ist, dass die Intermediate bereits vorhandene Werte überschreiben sollten.

Unten ist die Junkie-Idee, die ich bis jetzt habe. Ich kann die Listen im Vergleich zu Dicts und anderen Datentypen identifizieren und sie dort erstellen, wo sie nicht existieren. Ich sehe jedoch (a) nicht, wo ich den rekursiven Aufruf platzieren soll, oder (b) wie ich das tiefe Objekt "bilde", wenn ich die Liste durchlaufe, und (c) wie ich / probing / Ich unterscheiden kann tun, wie ich das Deep-Objekt von der / Setting / Ich muss tun, wenn ich das Ende des Stapels erreichen.

%Vor%

Das scheint sehr heikel zu sein, weil man irgendwie nachschauen muss, um den Typ des / next / -Objekts zu überprüfen, wenn man Platzhalter macht, und man muss nach hinten schauen, um einen Weg nach oben zu bauen .

AKTUALISIEREN

Ich hatte kürzlich Diese Frage beantwortet auch , die relevant oder hilfreich sein könnte.

    
Mittenchops 31.07.2013, 20:37
quelle

4 Antworten

2

Ich habe das in zwei Schritten aufgeteilt. Im ersten Schritt wird die Abfragezeichenfolge in eine Reihe von Anweisungen unterteilt. Auf diese Weise ist das Problem entkoppelt, wir können die Anweisungen vor der Ausführung anzeigen und es sind keine rekursiven Aufrufe erforderlich.

%Vor%

Für Ihr Beispiel

%Vor%

Der erwartete Rückgabewert ist einfach das _type (erstes Element) des nächsten Tupels in den Anweisungen. Dies ist sehr wichtig, weil wir damit fehlende Schlüssel korrekt initialisieren / rekonstruieren können.

Dies bedeutet, dass unsere erste Anweisung auf einem dict ausgeführt wird, entweder setzt oder ruft den Schlüssel 'f' , und es wird erwartet, dass ein list zurückgegeben wird. Ähnlich verhält es sich mit unserer zweiten Anweisung, die list verwendet, den Index 0 setzt oder setzt und einen dict zurückgibt.

Lassen Sie uns nun unsere Funktion _setattr erstellen. Dies ruft die richtigen Anweisungen auf und durchläuft sie, wobei erforderlichenfalls Schlüssel / Wert-Paare erstellt werden. Schließlich legt es auch val fest, die wir geben.

%Vor%

Und nur weil wir können, hier ist eine _getattr Funktion.

%Vor%

In Aktion:

%Vor%

Jetzt für ein paar coole Sachen. Im Gegensatz zu Python kann unsere _setattr -Funktion nicht abhebbare dict -Schlüssel setzen.

%Vor%

Wir sind nicht wirklich verwenden nicht aushashbare dict -Schlüssel, aber es sieht so aus, als wären wir in unserer Abfragesprache, also wen interessiert das, richtig?

Schließlich können Sie mehrere Aufrufe von _setattr für dasselbe Objekt ausführen, probieren Sie es einfach selbst aus.

    
FastTurtle 09.08.2013, 18:08
quelle
2

Sie können etwas zusammen hacken, indem Sie zwei Probleme beheben:

  1. Liste, die automatisch wächst, wenn außerhalb der Grenzen (PaddedList) zugegriffen wird
  2. Eine Möglichkeit, die Entscheidung, was erstellt werden soll (dict-Liste) zu verzögern, bis Sie das erste Mal darauf zugegriffen haben (DictOrList)

Der Code sieht dann so aus:

%Vor%

Und die Ausgabe von x und y wird sein:

%Vor%

Der Trick besteht darin, ein Standarddict von Objekten zu erstellen, die je nach Zugriff auf ein dict oder eine list sein können. Wenn Sie also die Zuordnung x['f'][10]['a'] = 'whatever' haben, funktioniert das folgendermaßen:

  1. Erhalte X ['f']. Es existiert nicht, so dass es ein DictOrList-Objekt für den Index 'f'
  2. zurückgibt
  3. Erhalte X ['f'] [10]. DictOrList. getitem wird mit einem Integer-Index aufgerufen. Das DictOrList-Objekt ersetzt sich in der übergeordneten Auflistung durch eine PaddedList
  4. Der Zugriff auf das 11. Element in der PaddedList wird um 11 Elemente erweitert und das MyDict-Element an dieser Position zurückgeben
  5. Weisen Sie x ['f'] [10] ['a']
  6. "was auch immer" zu

Sowohl PaddedList als auch DictOrList sind etwas hacky, aber nach all den Zuweisungen gibt es keine Magie mehr, Sie haben eine Struktur von Diktaten und Listen.

    
barracel 07.08.2013 12:54
quelle
1
%Vor%     
John La Rooy 31.07.2013 20:50
quelle
1

Es ist möglich, Elemente / Attribute rekursiv zu synthetisieren, indem Sie __getitem__ überschreiben, um einen Rückgabewert zurückzugeben, der einen Wert in der ursprünglichen Funktion setzen kann.

Ich arbeite gerade an einer Bibliothek, die ein paar ähnliche Dinge tut, also habe ich an einer Klasse gearbeitet, die dynamisch ihre eigenen Unterklassen bei der Instanziierung zuweisen kann. Es erleichtert das Arbeiten mit dieser Art von Dingen, aber wenn diese Art von Hacking Sie zimperlich macht, können Sie ein ähnliches Verhalten erzielen, indem Sie ein ProxyObject erstellen, das dem von mir erstellten ähnlich ist, und indem Sie die einzelnen vom ProxyObject verwendeten Klassen dynamisch erstellen . Etwas wie

%Vor%

Sie können ein Wörterbuch verwenden, wie ich es in FlexibleObject unten benutzt habe, würde diese Implementierung wesentlich effizienter machen, wenn dies die Art ist, wie Sie es implementieren. Der Code, den ich bereitstellen werde, verwendet jedoch das FlexibleObject. Momentan unterstützt es nur Klassen, die, wie fast alle von Pythons eingebauten Klassen, generiert werden können, indem eine Instanz von sich selbst als einziges Argument für ihre __init__ / __new__ genommen wird. In den nächsten ein oder zwei Wochen werde ich Unterstützung für alles Pickleable hinzufügen und auf ein Github-Repository verlinken, das es enthält. Hier ist der Code:

%Vor%

Wenn Sie dies verwenden möchten, um nachzuschließende Attribute und Elemente eines wörterbuchähnlichen Objekts zu synthetisieren, können Sie Folgendes tun:

%Vor%

Die Einzelheiten der Implementierung variieren je nachdem, was Sie wollen. Es ist offensichtlich wesentlich einfacher, __getitem__ im Proxy zu überschreiben, als es sowohl __getitem__ als auch __getattribute__ oder __getattr__ zu überschreiben. Die Syntax, die Sie in setbydot verwenden, lässt Sie so aussehen, als wären Sie am glücklichsten mit einer Lösung, die eine Mischung aus beiden außer Kraft setzt.

Wenn Sie nur das Wörterbuch verwenden, um Werte zu vergleichen, verwenden Sie =, & lt; =, & gt; = usw. Overriding __getattribute__ funktioniert wirklich gut. Wenn Sie etwas anspruchsvolleres tun möchten, sollten Sie besser __getattr__ überschreiben und einige Überprüfungen in __setattr__ durchführen, um zu bestimmen, ob Sie das Attribut synthetisieren möchten, indem Sie einen Wert im Wörterbuch festlegen oder möchten um das Attribut für den Artikel festzulegen, den Sie erhalten haben. Oder Sie möchten vielleicht damit umgehen, dass, wenn Ihr Objekt ein Attribut hat, __getattribute__ einen Proxy für dieses Attribut zurückgibt und __setattr__ immer nur das Attribut im Objekt setzt (in diesem Fall können Sie es komplett weglassen). All diese Dinge hängen davon ab, wofür Sie das Wörterbuch verwenden.

Sie können auch __iter__ und ähnliches erstellen. Es bedarf ein wenig Aufwand, um sie zu erstellen, aber die Details sollten aus der Implementierung von __getitem__ und __setitem__ folgen.

Schließlich werde ich kurz das Verhalten von QuickRecursiveDict zusammenfassen, falls es nicht sofort aus der Inspektion hervorgeht. Die try / excepts sind nur eine Kurzform, um zu überprüfen, ob die if s ausgeführt werden kann. Der einzige Hauptfehler bei der Synthese der rekursiven Einstellung besteht darin, dass Sie keine KeyErrors mehr auslösen können, wenn Sie versuchen, auf einen Schlüssel zuzugreifen, der nicht festgelegt wurde. Sie können jedoch ziemlich nahe kommen, indem Sie eine Unterklasse von KeyError zurückgeben, was ich im Beispiel mache. Ich habe es nicht getestet, also werde ich es dem Code nicht hinzufügen, aber Sie möchten möglicherweise eine lesbare Darstellung des Schlüssels für KeyError übergeben.

Aber abgesehen von allem funktioniert es ziemlich gut.

%Vor%

Noch eine Einschränkung, die Dinge, die zurückgegeben werden, sind nicht ganz so, wie sie aussehen. Es ist ein Proxy, wie es aussieht. Wenn du int 9 willst, brauchst du int(qrd[0][13]) nicht qrd[0][13] . Für Ints spielt dies keine Rolle, da, +, -, = und all diese Umgehung __getattribute__ , aber für Listen, würden Sie Attribute wie append verlieren, wenn Sie sie nicht umgeschrieben haben. (Sie würden len und andere eingebaute Methoden beibehalten, nur keine Attribute von list . Sie verlieren __len__ .)

Also das ist es. Der Code ist ein wenig verworren, also lassen Sie es mich wissen, wenn Sie irgendwelche Fragen haben. Ich kann sie wahrscheinlich erst heute Abend beantworten, wenn die Antwort nicht wirklich kurz ist. Ich wünschte, ich hätte diese Frage früher gesehen, es ist eine wirklich coole Frage, und ich werde bald versuchen, eine sauberere Lösung zu aktualisieren. Ich hatte Spaß daran, eine Lösung bis in die frühen Morgenstunden zu programmieren. :)

    
user1612868 13.08.2013 16:41
quelle