So verwenden Sie einen Punkt "." auf Mitglieder des Wörterbuchs zugreifen?

282

Wie mache ich Python-Wörterbuchmitglieder über einen Punkt "." Zugänglich?

Zum Beispiel mydict['val']möchte ich schreiben , anstatt zu schreiben mydict.val.

Außerdem möchte ich auf diese Weise auf verschachtelte Diktate zugreifen. Beispielsweise

mydict.mydict2.val 

würde sich beziehen

mydict = { 'mydict2': { 'val': ... } }
bodacydo
quelle
20
Viele der Situationen, in denen Menschen verschachtelte Diktate verwenden, werden genauso gut oder besser von Diktaten mit Tupeln als Schlüsseln bedient, d[a][b][c]die durch ersetzt werden d[a, b, c].
Mike Graham
7
Es ist keine Magie: foo = {}; foo [1,2,3] = "eins, zwei, drei!"; foo.keys () => [(1,2,3)]
Bryan Oakley
10
Beeindruckend. Wow schon wieder. Ich wusste nicht, dass Tupel Schlüssel des Diktats sein können. Wow zum dritten Mal.
Bodacydo
3
Jedes Objekt, das "hashbar" ist, kann als Schlüssel eines Diktats verwendet werden. Die meisten unveränderlichen Objekte sind auch hashbar, jedoch nur, wenn ihr gesamter Inhalt hashbar ist. Der Code d [1, 2, 3] funktioniert, weil "," der "Tupeloperator erstellen" ist. es ist dasselbe wie d [(1, 2, 3)]. Um die Deklaration eines Tupels herum sind häufig Klammern optional.
Larry Hastings
6
Haben Sie den Fall in Betracht gezogen, in dem der Schlüssel einen Punkt für sich hat - {"my.key":"value"}? Oder wenn der Schlüssel ein Schlüsselwort ist, wie "von"? Ich habe ein paar Mal darüber nachgedacht, und es sind mehr Probleme und Fehlerbehebung als wahrgenommene Vorteile.
Todor Minakov

Antworten:

147

Sie können es mit dieser Klasse machen, die ich gerade gemacht habe. Mit dieser Klasse können Sie das MapObjekt wie ein anderes Wörterbuch (einschließlich JSON-Serialisierung) oder mit der Punktnotation verwenden. Ich hoffe Dir zu helfen:

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Anwendungsbeispiele:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']
epool
quelle
21
Um an Python 3 zu arbeiten, habe ich .iteritems()auf.items()
Berto
13
Beachten Sie, dass sich dies anders verhält als die üblichen Erwartungen, da es nicht ausgelöst wird, AttributeErrorwenn das Attribut nicht vorhanden ist. Stattdessen wird es zurückkehren None.
mic_e
Empfehlen Sie das Hinzufügen von getstate und setstate, damit Deep Copy und andere Systeme dies unterstützen können.
user1363990
4
Sie können Ihren Konstruktor vereinfachen self.update(*args,**kwargs). Sie können auch hinzufügen __missing__(self,key): value=self[key]= type(self)(); return value. Anschließend können Sie fehlende Einträge in Punktnotation hinzufügen. Wenn Sie möchten, dass es auswählbar ist, können Sie hinzufügen __getstate__und__setstate__
Jens Munk
1
Dies würde hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding __getattr__`
Xiao
264

Ich habe das immer in einer Util-Datei aufbewahrt. Sie können es auch als Mixin für Ihre eigenen Klassen verwenden.

class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'

mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'
derek73
quelle
5
Sehr einfache Antwort, toll! Wissen Sie zufällig, was ich tun muss, damit Tabyth in IPython funktioniert? Die Klasse müsste __dir __ (self) implementieren, aber irgendwie kann ich es nicht zum Laufen bringen.
andreas-h
8
+1 der Einfachheit halber. scheint aber nicht an verschachtelten Diktaten zu funktionieren. d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.barlöst einen Attributfehler aus, d.foofunktioniert aber einwandfrei.
tmthyjames
2
Ja, dies funktioniert nicht für komplexe verschachtelte Strukturen.
David
16
@tmthyjames Sie könnten einfach ein Objekt vom Typ dotdict in der Getter-Methode zurückgeben, um rekursiv auf Attribute mit Punktnotation zuzugreifen, wie: python class DotDict(dict): """dot.notation access to dictionary attributes""" def __getattr__(*args): val = dict.get(*args) return DotDict(val) if type(val) is dict else val __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__
TMKasun
4
Nach dem Experimentieren scheint getes in der Tat eine schlechte Idee zu sein, da es zurückkehren wird, Noneanstatt einen Fehler für fehlende Gegenstände
auszulösen
117

Installieren dotmapüberpip

pip install dotmap

Es macht alles, was Sie wollen, und Unterklassen dict, so dass es wie ein normales Wörterbuch funktioniert:

from dotmap import DotMap

m = DotMap()
m.hello = 'world'
m.hello
m.hello += '!'
# m.hello and m['hello'] now both return 'world!'
m.val = 5
m.val2 = 'Sam'

Darüber hinaus können Sie es in und aus dictObjekten konvertieren :

d = m.toDict()
m = DotMap(d) # automatic conversion in constructor

Das heißt, wenn etwas, auf das Sie zugreifen möchten, bereits in dictForm ist, können Sie es in einen DotMapfür den einfachen Zugriff umwandeln:

import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

Schließlich werden automatisch neue untergeordnete DotMapInstanzen erstellt, sodass Sie folgende Aktionen ausführen können:

m = DotMap()
m.people.steve.age = 31

Vergleich mit Bunch

Vollständige Offenlegung: Ich bin der Schöpfer der DotMap . Ich habe es erstellt, weil Bunchdiese Funktionen fehlten

  • Das Erinnern an die Bestellpositionen wird hinzugefügt und in dieser Reihenfolge wiederholt
  • Automatische untergeordnete DotMapErstellung, die Zeit spart und saubereren Code sorgt, wenn Sie viel Hierarchie haben
  • Konstruieren aus a dictund rekursives Konvertieren aller untergeordneten dictInstanzen inDotMap
Chris Redford
quelle
2
:-) Kannst du es mit Schlüsseln zum Laufen bringen, die bereits einen Namen im Namen haben? {"test.foo": "bar"}kann über erreicht werden mymap.test.fooDas wäre fantastisch. Es wird einige Regressionen erfordern, um eine flache Karte in eine tiefe Karte umzuwandeln und dann DotMap darauf anzuwenden, aber es lohnt sich!
dlite922
Ordentlich. Gibt es eine Möglichkeit, die Auflistung / Vervollständigung von Registerkarten mit den Tasten im Jupyter-Notizbuch zum Laufen zu bringen? Der punktförmige Zugriff ist für die interaktive Verwendung am wertvollsten.
Dmitri
@Dmitri Cooles Produkt. Ich habe noch nie davon gehört, daher bin ich mir nicht sicher, wie die automatische Vervollständigung funktionieren soll. Ich bin damit einverstanden, dass die Verwendung DotMapmit Autocomplete am besten funktioniert. Ich verwende Sublime Text, der zuvor eingegebene Schlüsselwörter automatisch vervollständigt.
Chris Redford
1
Ich finde, dass es keine Wörterbuchextraktion für Dinge wie **kwargsoder gibt c = {**a, **b}. Tatsächlich schlägt es leise fehl und verhält sich beim Extrahieren wie ein leeres Wörterbuch.
Simon Streicher
@ SimonStreicher Ich habe das mit getestet m = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m));und ich habe das erwartet bekommen 2 3. Wenn Sie einen nachweislich fehlerhaften Fall haben, der funktioniert, dict()aber nicht DotMap(), senden Sie Ihren Code bitte an die Registerkarte Probleme in GitHub.
Chris Redford
56

Ableiten von diktieren und implementieren __getattr__und __setattr__.

Oder Sie können Bunch verwenden, das sehr ähnlich ist.

Ich denke nicht, dass es möglich ist, eine eingebaute Diktatklasse zu monkeypatchen.

Kugel
quelle
2
Was bedeutet Monkeypatch genau? Ich habe davon gehört, aber nicht benutzt. (Tut mir leid, dass ich solchen Neulingen Fragen stelle, ich bin noch nicht so gut im Programmieren (ich bin nur Student im 2. Jahr.))
bodacydo
9
Monkeypatching verwendet die Dynamik von Python (oder einer anderen Sprache), um etwas zu ändern, das normalerweise im Quellcode definiert wird. Dies gilt insbesondere für das Ändern der Definition von Klassen nach ihrer Erstellung.
Mike Graham
Wenn Sie diese Funktionalität häufig verwenden, achten Sie auf die Geschwindigkeit von Bunch. Ich habe es ziemlich häufig verwendet und es hat ein Drittel meiner Anforderungszeit in Anspruch genommen. Schauen Sie sich meine Antwort an, um eine detailliertere Erklärung dafür zu erhalten.
JayD3e
22

Stoff hat eine wirklich schöne, minimale Implementierung . Wenn Sie dies erweitern, um einen verschachtelten Zugriff zu ermöglichen, können Sie a verwenden. defaultdictDas Ergebnis sieht ungefähr so ​​aus:

from collections import defaultdict

class AttributeDict(defaultdict):
    def __init__(self):
        super(AttributeDict, self).__init__(AttributeDict)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(key)

    def __setattr__(self, key, value):
        self[key] = value

Nutzen Sie es wie folgt:

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234

Das geht ein wenig auf Kugel's Antwort von ein "Ableiten von diktieren und implementieren __getattr__und __setattr__" ein. Jetzt weißt du wie!

Dave
quelle
1
Das ist großartig!
Thomas Klinger
Es ist schön, ein Standarddiktat aufzunehmen - dies scheint jedoch nur zu funktionieren, wenn ein Diktat von Grund auf neu gestartet wird. Wenn wir ein vorhandenes Diktat rekursiv in ein "Dotdict" konvertieren müssen. Hier ist eine Alternative dotdict, mit der vorhandene dictObjekte rekursiv konvertiert werden können: gist.github.com/miku/…
miku
19

Ich habe es versucht:

class dotdict(dict):
    def __getattr__(self, name):
        return self[name]

Du kannst es versuchen __getattribute__ .

Machen Sie jedes Diktat zu einer Art Dotdict. Wenn Sie dies aus einem mehrschichtigen Diktat heraus initiieren möchten, versuchen Sie es __init__ebenfalls.

tdihp
quelle
oops, @Kugels Antwort ist ähnlich.
tdihp
1
tdihp, ich mag deine Antwort immer noch, weil ich sie schneller verstanden habe - sie hat den eigentlichen Code.
Yigal
1
+1 für den tatsächlichen Code. Aber auch der Vorschlag von @ Kugel, Bunch zu verwenden, ist sehr gut.
Dannid
Ich fand es nützlich, dies in eine Funktion einzubetten, indem ich sie def docdict(name):davor platziere und dann "if isinstance (name, dict): DotDict (name) return name"
Daniel Moskovich,
tolles einfaches Beispiel .. Ich habe dies ein wenig erweitert, so dass ein verschachteltes Diktat leicht verkettet werden kann, ähnlich wie @DanielMoskovich, aber auch Blattknoten korrekt für int, string usw. zurückgibt oder null, wenn es nicht gefunden wirdclass dotdict(dict): def __getattr__(self, name): if name not in self: return None elif type(self[name]) is dict: return JsonDot(self[name]) else: return self[name]
D Sievers
11

Tu es nicht. Attributzugriff und Indizierung sind in Python getrennte Dinge, und Sie sollten nicht möchten, dass sie dieselbe Leistung erbringen. Erstellen Sie eine Klasse (möglicherweise eine von namedtuple), wenn Sie etwas haben, das über zugängliche Attribute verfügen sollte, und verwenden Sie die []Notation, um ein Element aus einem Diktat zu erhalten.

Mike Graham
quelle
Danke für die Antwort. Aber werfen Sie einen Blick auf diese Frage, die ich auch gerade gestellt habe: stackoverflow.com/questions/2352252/… Dies scheint eine gute Idee zu sein, .anstatt []auf komplizierte Datenstrukturen in Mako-Vorlagen zuzugreifen.
Bodacydo
2
Ich kann einen Anwendungsfall dafür sehen; Tatsächlich habe ich es erst vor ein paar Wochen gemacht. In meinem Fall wollte ich ein Objekt, auf das ich mit Punktnotation zugreifen kann. Ich fand es sehr einfach, einfach von dict zu erben, damit alle dikt-Funktionen integriert sind, aber die öffentliche Schnittstelle zu diesem Objekt verwendet die Punktnotation (es ist im Wesentlichen eine schreibgeschützte Schnittstelle zu einigen statischen Daten). Meine Benutzer sind mit 'foo.bar' viel zufriedener als mit 'foo ["bar"]' und ich bin froh, dass ich die Funktionen des Diktattyps diktieren kann.
Bryan Oakley
10
Sie kennen bereits einen guten Python-Stil: Wir sagen Ihnen, tun Sie nicht so, als wären die Werte eines Diktats Attribute. Es ist eine schlechte Praxis. Was ist beispielsweise, wenn Sie einen Wert mit demselben Namen wie ein vorhandenes Attribut eines Diktats speichern möchten, z. B. "items" oder "get" oder "pop"? Wahrscheinlich etwas verwirrendes. Also tu es nicht!
Larry Hastings
5
Hoppla, ich habe Attribute wie 'items', 'get' oder 'pop' vergessen. Vielen Dank, dass Sie dieses wichtige Beispiel angesprochen haben!
Bodacydo
5
@Gabe, es ist lange her ... aber ich denke, es lohnt sich zu sagen. Es ist nicht "gut genug in JS": Es ist "schrecklich genug in JS". Es wird lustig, wenn Sie Schlüssel / Attribute speichern, die denselben Namen wie andere wichtige Attribute in der Prototypenkette haben.
Bgusach
11

Wenn Sie Ihr modifiziertes Wörterbuch auswählen möchten, müssen Sie den obigen Antworten einige Statusmethoden hinzufügen:

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    def __getattr__(self, attr):
        return self.get(attr)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __getstate__(self):
        return self

    def __setstate__(self, state):
        self.update(state)
        self.__dict__ = self
volodymyr
quelle
Danke für den Kommentar zum Beizen. Ich war verrückt nach diesem Fehler und merkte nur, dass es an diesem Problem lag!
Shagru
Tritt auch auf, wenn Sie copy.deepcopy verwenden. Dieser Zusatz wird benötigt.
user1363990
Vereinfachung:__getattr__ = dict.get
Martineau
9

Aufbauend auf Kugel's Antwort und unter Berücksichtigung von Mike Grahams warnenden Worten, was ist, wenn wir eine Verpackung herstellen?

class DictWrap(object):
  """ Wrap an existing dict, or create a new one, and access with either dot 
    notation or key lookup.

    The attribute _data is reserved and stores the underlying dictionary.
    When using the += operator with create=True, the empty nested dict is 
    replaced with the operand, effectively creating a default dictionary
    of mixed types.

    args:
      d({}): Existing dict to wrap, an empty dict is created by default
      create(True): Create an empty, nested dict instead of raising a KeyError

    example:
      >>>dw = DictWrap({'pp':3})
      >>>dw.a.b += 2
      >>>dw.a.b += 2
      >>>dw.a['c'] += 'Hello'
      >>>dw.a['c'] += ' World'
      >>>dw.a.d
      >>>print dw._data
      {'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}

  """

  def __init__(self, d=None, create=True):
    if d is None:
      d = {}
    supr = super(DictWrap, self)  
    supr.__setattr__('_data', d)
    supr.__setattr__('__create', create)

  def __getattr__(self, name):
    try:
      value = self._data[name]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[name] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setattr__(self, name, value):
    self._data[name] = value  

  def __getitem__(self, key):
    try:
      value = self._data[key]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[key] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setitem__(self, key, value):
    self._data[key] = value

  def __iadd__(self, other):
    if self._data:
      raise TypeError("A Nested dict will only be replaced if it's empty")
    else:
      return other
MJ
quelle
8

Verwendung SimpleNamespace:

>>> from types import SimpleNamespace   
>>> d = dict(x=[1, 2], y=['a', 'b'])
>>> ns = SimpleNamespace(**d)
>>> ns.x
[1, 2]
>>> ns
namespace(x=[1, 2], y=['a', 'b'])
Dmitry Zotikov
quelle
1
Dieser Ansatz funktioniert besser. (mit json aus Datei geladen)
ged
Berücksichtigt dies verschachtelte Diktate?
Mojimi
1
Verschachteltes Diktat nicht unterstützen. docs.python.org/3.3/library/types.html#types.SimpleNamespace
Carson
6

Ich mag den Munch und er bietet viele praktische Optionen zusätzlich zum Punktzugriff.

Munch importieren

temp_1 = {'person': {'fname': 'senthil', 'lname': 'ramalingam'}}

dict_munch = munch.munchify (temp_1)

dict_munch.person.fname

Senthil
quelle
6

Ich bin kürzlich auf die Box gestoßen -Bibliothek , die das Gleiche tut.

Installationsbefehl: pip install python-box

Beispiel:

from box import Box

mydict = {"key1":{"v1":0.375,
                    "v2":0.625},
          "key2":0.125,
          }
mydict = Box(mydict)

print(mydict.key1.v1)

Ich fand es effektiver als andere vorhandene Bibliotheken wie Dotmap, die Python-Rekursionsfehler erzeugen, wenn Sie große verschachtelte Dicts haben.

Link zur Bibliothek und Details: https://pypi.org/project/python-box/

Pradip Gupta
quelle
5

Die Verwendung __getattr__funktioniert sehr einfach in Python 3.4.3

class myDict(dict):
    def __getattr__(self,val):
        return self[val]


blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)

Ausgabe:

10000
StackOverflow
IRSHAD
quelle
4

Die Sprache selbst unterstützt dies nicht, aber manchmal ist dies immer noch eine nützliche Voraussetzung. Neben dem Bunch-Rezept können Sie auch eine kleine Methode schreiben, mit der Sie mithilfe einer gepunkteten Zeichenfolge auf ein Wörterbuch zugreifen können:

def get_var(input_dict, accessor_string):
    """Gets data from a dictionary using a dotted accessor-string"""
    current_data = input_dict
    for chunk in accessor_string.split('.'):
        current_data = current_data.get(chunk, {})
    return current_data

was so etwas unterstützen würde:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>
pbanka
quelle
4

Um auf der Antwort von epool aufzubauen, können Sie mit dieser Version über den Punktoperator auf jedes Diktat im Inneren zugreifen:

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
    }
}

Gibt zum Beispiel foo.bar.baz[1].babazurück "loo".

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    if isinstance(v, dict):
                        v = Map(v)
                    if isinstance(v, list):
                        self.__convert(v)
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                if isinstance(v, dict):
                    v = Map(v)
                elif isinstance(v, list):
                    self.__convert(v)
                self[k] = v

    def __convert(self, v):
        for elem in xrange(0, len(v)):
            if isinstance(v[elem], dict):
                v[elem] = Map(v[elem])
            elif isinstance(v[elem], list):
                self.__convert(v[elem])

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]
berühre meinen Körper
quelle
1
Python 3: Ersetzen iteritems()durch items()und xrange()mitrange()
sasawatc
3
def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

    class Struct:
        def __init__(self, **entries):
            self.__dict__.update(entries)

    return Struct(**dick)

Wenn man sich entscheidet, das dauerhaft dictin ein Objekt umzuwandeln, sollte dies reichen. Sie können unmittelbar vor dem Zugriff ein Wegwerfobjekt erstellen.

d = dict_to_object(d)
nehem
quelle
def attr (** kwargs): o = lambda: Keine o .__ dict __. update (** kwargs) return o
throw_exceptions_at_you
2

Am Ende habe ich sowohl das AttrDict als auch das Bunch ausprobiertBibliotheken und fand sie viel zu langsam für meine Zwecke. Nachdem ein Freund und ich uns damit befasst hatten, stellten wir fest, dass die Hauptmethode zum Schreiben dieser Bibliotheken dazu führt, dass die Bibliothek aggressiv durch ein verschachteltes Objekt rekursiert und durchgehend Kopien des Wörterbuchobjekts erstellt. Vor diesem Hintergrund haben wir zwei wichtige Änderungen vorgenommen. 1) Wir haben Attribute faul geladen. 2) Anstatt Kopien eines Wörterbuchobjekts zu erstellen, erstellen wir Kopien eines leichten Proxy-Objekts. Dies ist die endgültige Implementierung. Die Leistungssteigerung bei der Verwendung dieses Codes ist unglaublich. Bei Verwendung von AttrDict oder Bunch haben diese beiden Bibliotheken allein 1/2 bzw. 1/3 meiner Anforderungszeit in Anspruch genommen (was!?). Dieser Code reduzierte diese Zeit auf fast nichts (irgendwo im Bereich von 0,5 ms). Dies hängt natürlich von Ihren Anforderungen ab, aber wenn Sie diese Funktionalität in Ihrem Code häufig verwenden,

class DictProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    def __getattr__(self, key):
        try:
            return wrap(getattr(self.obj, key))
        except AttributeError:
            try:
                return self[key]
            except KeyError:
                raise AttributeError(key)

    # you probably also want to proxy important list properties along like
    # items(), iteritems() and __len__

class ListProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    # you probably also want to proxy important list properties along like
    # __iter__ and __len__

def wrap(value):
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (tuple, list)):
        return ListProxy(value)
    return value

Die ursprüngliche Implementierung finden Sie hier unter https://stackoverflow.com/users/704327/michael-merickel .

Die andere Sache zu beachten ist, dass diese Implementierung ziemlich einfach ist und nicht alle Methoden implementiert, die Sie möglicherweise benötigen. Sie müssen diese nach Bedarf in die DictProxy- oder ListProxy-Objekte schreiben.

JayD3e
quelle
0

Ich möchte meine eigene Lösung in den Ring werfen:

https://github.com/skorokithakis/jsane

Sie können JSON in etwas analysieren, auf das Sie zugreifen können with.attribute.lookups.like.this.r(), hauptsächlich, weil ich diese Antwort nicht gesehen habe, bevor ich mit der Arbeit begonnen habe.

Stavros Korokithakis
quelle
Python ist an einigen lästigen einfachen Designfehlern schuld. Das Erhöhen KeyErrorist einer davon. Wenn man auf den Schlüssel zugreift, der nicht existiert, muss man nur Noneähnlich wie JS zurückgeben. Ich bin ein großer Fan von Autovivifizierung sowohl zum Lesen als auch zum Schreiben. Ihre Bibliothek ist dem Ideal am nächsten.
Nehem
0

Keine direkte Antwort auf die Frage des OP, aber inspiriert von und vielleicht nützlich für einige. Ich habe eine objektbasierte Lösung unter Verwendung des internen __dict__(in keiner Weise optimierten Codes) erstellt.

payload = {
    "name": "John",
    "location": {
        "lat": 53.12312312,
        "long": 43.21345112
    },
    "numbers": [
        {
            "role": "home",
            "number": "070-12345678"
        },
        {
            "role": "office",
            "number": "070-12345679"
        }
    ]
}


class Map(object):
    """
    Dot style access to object members, access raw values
    with an underscore e.g.

    class Foo(Map):
        def foo(self):
            return self.get('foo') + 'bar'

    obj = Foo(**{'foo': 'foo'})

    obj.foo => 'foobar'
    obj._foo => 'foo'

    """

    def __init__(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self.__dict__[k] = v
                    self.__dict__['_' + k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self.__dict__[k] = v
                self.__dict__['_' + k] = v

    def __getattribute__(self, attr):
        if hasattr(self, 'get_' + attr):
            return object.__getattribute__(self, 'get_' + attr)()
        else:
            return object.__getattribute__(self, attr)

    def get(self, key):
        try:
            return self.__dict__.get('get_' + key)()
        except (AttributeError, TypeError):
            return self.__dict__.get(key)

    def __repr__(self):
        return u"<{name} object>".format(
            name=self.__class__.__name__
        )


class Number(Map):
    def get_role(self):
        return self.get('role')

    def get_number(self):
        return self.get('number')


class Location(Map):
    def get_latitude(self):
        return self.get('lat') + 1

    def get_longitude(self):
        return self.get('long') + 1


class Item(Map):
    def get_name(self):
        return self.get('name') + " Doe"

    def get_location(self):
        return Location(**self.get('location'))

    def get_numbers(self):
        return [Number(**n) for n in self.get('numbers')]


# Tests

obj = Item({'foo': 'bar'}, **payload)

assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112

for n in obj.numbers:
    assert type(n) == Number
    if n.role == 'home':
        assert n.number == "070-12345678"
    if n.role == 'office':
        assert n.number == "070-12345679"
Hedde van der Heide
quelle
0

Eine einfache Möglichkeit, Punktzugriff (aber keinen Arrayzugriff) zu erhalten, besteht darin, ein einfaches Objekt in Python zu verwenden. So was:

class YourObject:
    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

... und benutze es so:

>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"

... um es in ein Diktat umzuwandeln:

>>> print(obj.__dict__)
{"key": "value"}
Emil Stenström
quelle
0

Diese Lösung ist eine Weiterentwicklung der von epool angebotenen Lösung , um die Anforderung des OP zu erfüllen, auf konsistente Weise auf verschachtelte Dikte zuzugreifen. Die Lösung von epool erlaubte keinen Zugriff auf verschachtelte Dikte.

class YAMLobj(dict):
    def __init__(self, args):
        super(YAMLobj, self).__init__(args)
        if isinstance(args, dict):
            for k, v in args.iteritems():
                if not isinstance(v, dict):
                    self[k] = v
                else:
                    self.__setattr__(k, YAMLobj(v))


    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(YAMLobj, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(YAMLobj, self).__delitem__(key)
        del self.__dict__[key]

Mit dieser Klasse kann man jetzt so etwas machen wie : A.B.C.D.

deepak
quelle
0

Dies funktioniert auch mit verschachtelten Dikten und stellt sicher, dass sich später angehängte Dikte gleich verhalten:

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Recursively turn nested dicts into DotDicts
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__
Yaniv K.
quelle
0

Die Antwort von @ derek73 ist sehr ordentlich, kann jedoch weder eingelegt noch (tief) kopiert werden und wird Nonefür fehlende Schlüssel zurückgegeben. Der folgende Code behebt dies.

Bearbeiten: Ich habe die Antwort oben nicht gesehen , die genau den gleichen Punkt anspricht (positiv bewertet). Ich lasse die Antwort hier als Referenz.

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)
marnix
quelle
-1

Eine Lösung, die irgendwie heikel ist

class DotDict(dict):

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, key):

        def typer(candidate):
            if isinstance(candidate, dict):
                return DotDict(candidate)

            if isinstance(candidate, str):  # iterable but no need to iter
                return candidate

            try:  # other iterable are processed as list
                return [typer(item) for item in candidate]
            except TypeError:
                return candidate

            return candidate

        return typer(dict.get(self, key))
Yonks Somarl
quelle