417 Stimmen

Zugriff auf Diktatschlüssel wie ein Attribut?

Ich finde es bequemer, auf die Diktatschlüssel als obj.foo anstelle von obj['foo'] also habe ich diesen Ausschnitt geschrieben:

class AttributeDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

Ich nehme jedoch an, dass es einen Grund dafür geben muss, dass Python diese Funktionalität nicht von Haus aus bereitstellt. Was wären die Vorbehalte und Fallstricke beim Zugriff auf dict-Schlüssel auf diese Weise?

24voto

DeusXMachina Punkte 1114

Ich fand mich fragen, was der aktuelle Stand der "dict Schlüssel als attr" in der Python Ökosystem. Wie mehrere Kommentatoren darauf hingewiesen haben, ist dies wahrscheinlich nicht etwas, das Sie von Grund auf selbst herstellen wollen Denn es gibt mehrere Fallstricke und Fußangeln, von denen einige sehr subtil sind. Außerdem würde ich nicht empfehlen, die Namespace als Basisklasse, ich habe das schon erlebt, es ist nicht schön.

Glücklicherweise gibt es mehrere Open-Source-Pakete, die diese Funktionalität bereitstellen und per Pip installiert werden können! Leider gibt es mehrere Pakete. Hier ist eine Übersicht, Stand Dezember 2019.

Anwärter (jüngste Übergabe an Master|#commits|#contribs|coverage%):

Nicht mehr oder zu wenig gewartet:

  • treedict (2014-03-28 | 95 | 2 | ?%)
  • Bündel (2012-03-12 | 20 | 2 | ?%)
  • NeoBunch

Ich empfehle derzeit mampfen o süchtig . Sie haben die meisten Commits, Mitwirkenden und Veröffentlichungen, was auf eine gesunde Open-Source-Codebasis für beide schließen lässt. Sie haben die am saubersten aussehende readme.md, 100% Abdeckung und gut aussehende Tests.

Ich habe keinen Hund in diesem Rennen (vorerst!), abgesehen davon, dass ich meinen eigenen dict/attr-Code entwickelt habe und eine Menge Zeit verschwendet habe, weil ich mir all dieser Optionen nicht bewusst war :). Ich werde vielleicht in Zukunft zu addict/munch beitragen, da ich lieber ein solides Paket als einen Haufen fragmentierter Pakete sehen würde. Wenn Sie sie mögen, tragen Sie bei! Insbesondere sieht es so aus, als könnte munch ein Codecov-Abzeichen und addict ein Python-Versions-Abzeichen gebrauchen.

süchtige Profis:

  • Rekursive Initialisierung (foo.a.b.c = 'bar'), diktatähnliche Argumente werden zu addict.Dict

Süchtige Knackis:

  • schatten typing.Dict wenn Sie from addict import Dict
  • Keine Schlüsselkontrolle. Wenn Sie einen Schlüssel falsch schreiben, erstellen Sie aufgrund der rekursiven Init-Erlaubnis einfach ein neues Attribut, anstatt einen KeyError (danke AljoSt)

Munch-Profis:

  • eindeutige Namensgebung
  • integrierte ser/de-Funktionen für JSON und YAML

munch cons:

  • kein rekursives init (Sie können nicht konstruieren foo.a.b.c = 'bar' müssen Sie einstellen foo.a entonces foo.a.b , usw.

Wo ich ein Editorial schreibe

Vor vielen Monden, als ich noch Texteditoren zum Schreiben von Python verwendete, mochte ich den Stil von dict-attrs, die Möglichkeit, Schlüssel durch einfaches Deklarieren einzufügen foo.bar.spam = eggs . Jetzt arbeite ich in Teams und verwende eine IDE für alles, und ich habe mich von dieser Art von Datenstrukturen und der dynamischen Typisierung im Allgemeinen entfernt, zugunsten von statischer Analyse, funktionalen Techniken und Typ-Hinweisen. Ich habe angefangen, mit dieser Technik zu experimentieren, indem ich Pstruct mit Objekten meines eigenen Designs untergliedert habe:

class  BasePstruct(dict):
    def __getattr__(self, name):
        if name in self.__slots__:
            return self[name]
        return self.__getattribute__(name)

    def __setattr__(self, key, value):
        if key in self.__slots__:
            self[key] = value
            return
        if key in type(self).__dict__:
            self[key] = value
            return
        raise AttributeError(
            "type object '{}' has no attribute '{}'".format(type(self).__name__, key))

class FooPstruct(BasePstruct):
    __slots__ = ['foo', 'bar']

Dadurch erhalten Sie ein Objekt, das sich immer noch wie ein Diktat verhält, aber Sie können auch auf Schlüssel wie Attribute zugreifen, und zwar auf eine viel starrere Weise. Der Vorteil dabei ist, dass ich (oder die unglücklichen Nutzer Ihres Codes) genau weiß, welche Felder existieren können und welche nicht, und die IDE kann Felder automatisch vervollständigen. Auch die Unterklassifizierung von vanilla dict bedeutet, dass die json-Serialisierung einfach ist. Ich denke, die nächste Entwicklung in dieser Idee wäre eine benutzerdefinierte Protobuf-Generator, der diese Schnittstellen emittiert, und eine nette knock-on ist Sie erhalten sprachübergreifende Datenstrukturen und IPC über gRPC für fast kostenlos.

Wenn Sie sich für attr-dicts entscheiden, sollten Sie unbedingt dokumentieren, welche Felder erwartet werden, damit Sie selbst (und Ihre Teamkollegen) vernünftig bleiben.

Sie können diesen Beitrag gerne bearbeiten/aktualisieren, um ihn aktuell zu halten!

24voto

Ryan Punkte 596

Vorbehalt des Leergangs: Aus irgendeinem Grund scheinen Klassen wie diese das Multiprocessing-Paket zu sprengen. Ich kämpfte eine Weile mit diesem Fehler, bevor ich dieses SO fand: Suche nach Ausnahmen in Python-Multiprozessoren

18voto

The Communist Duck Punkte 5901

Was wäre, wenn Sie einen Schlüssel benötigen, der eine Methode ist, wie z. B. __eq__ o __getattr__ ?

Und Sie könnten keinen Eintrag haben, der nicht mit einem Buchstaben beginnt, also verwenden Sie 0343853 als Schlüssel ist aus.

Und was wäre, wenn Sie keine Zeichenkette verwenden wollten?

13voto

Senthil Kumaran Punkte 50813

Tupel können als Diktatschlüssel verwendet werden. Wie würden Sie auf Tupel in Ihrem Konstrukt zugreifen?

Auch, namedtuple ist eine praktische Struktur, die über den Attributzugriff Werte liefern kann.

11voto

ramazan polat Punkte 5829

Wie wäre es mit Prodikt , die kleine Python-Klasse, die Ich schrieb um sie alle zu beherrschen:)

Außerdem erhalten Sie Automatische Code-Vervollständigung , rekursive Objektinstanziierungen et automatische Typenumwandlung !

Sie können genau das tun, worum Sie gebeten haben:

p = Prodict()
p.foo = 1
p.bar = "baz"

Beispiel 1: Typ Hinting

class Country(Prodict):
    name: str
    population: int

turkey = Country()
turkey.name = 'Turkey'
turkey.population = 79814871

auto code complete

Beispiel 2: Automatische Typkonvertierung

germany = Country(name='Germany', population='82175700', flag_colors=['black', 'red', 'yellow'])

print(germany.population)  # 82175700
print(type(germany.population))  # <class 'int'>

print(germany.flag_colors)  # ['black', 'red', 'yellow']
print(type(germany.flag_colors))  # <class 'list'>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X