657 Stimmen

Verschachtelte Python-Diktate in Objekte umwandeln?

Ich bin auf der Suche nach einem eleganten Weg, um Daten mit Attribut Zugriff auf ein Diktat mit einigen verschachtelten Dicts und Listen (d.h. Javascript-Stil Objekt-Syntax) zu erhalten.

Zum Beispiel:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}

Sollte auf diese Weise zugänglich sein:

>>> x = dict2obj(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
bar

Ich denke, dies ist nicht möglich, ohne Rekursion, aber was wäre ein schöner Weg, um ein Objekt-Stil für Dicts zu erhalten?

6 Stimmen

Ich habe vor kurzem versucht, etwas Ähnliches zu tun, aber ein wiederkehrender Wörterbuchschlüssel ("from" - ein Python-Schlüsselwort) hat mich daran gehindert, es zu tun. Denn sobald man versuchte, mit "x.from" auf dieses Attribut zuzugreifen, erhielt man einen Syntaxfehler.

3 Stimmen

Das ist in der Tat ein Problem, aber ich kann auf "from" verzichten, um das Leben einfacher zu machen, wenn ich auf große Diktatkonstrukte zugreife:) die Eingabe von x['a']['d'][1]['foo'] ist wirklich lästig, also gilt x.a.d[1].foo. wenn Sie from benötigen, können Sie über getattr(x, 'from') darauf zugreifen oder stattdessen _from als Attribut verwenden.

7 Stimmen

from_ statt _from laut PEP 8 .

58voto

umbrae Punkte 1015

Es gibt eine Sammlungshelfer namens namedtuple die dies für Sie tun können:

from collections import namedtuple

d_named = namedtuple('Struct', d.keys())(*d.values())

In [7]: d_named
Out[7]: Struct(a=1, b={'c': 2}, d=['hi', {'foo': 'bar'}])

In [8]: d_named.a
Out[8]: 1

38 Stimmen

Damit ist die Frage der Rekursion für die verschachtelten Dicts noch nicht beantwortet.

7 Stimmen

Können Sie Nameduples nicht ändern.

0 Stimmen

Können wir garantieren, dass die Reihenfolge der Liste, die von keys() und values() zurückgegeben wird, der Reihenfolge nach übereinstimmt? Ich meine, wenn es ein OrderedDict ist, ja. Aber ein Standard-Dict? Ich weiß, dass CPython 3.6+ und PyPy "compact" dicts geordnet sind, aber ich zitiere die Dokumentation: "Der ordnungserhaltende Aspekt dieser neuen Implementierung ist ein Implementierungsdetail und sollte nicht als verlässlich angesehen werden.

38voto

DS. Punkte 20324

Wenn Ihr Diktat von json.loads() können Sie es stattdessen in einer Zeile in ein Objekt (und nicht in ein Diktat) verwandeln:

import json
from collections import namedtuple

json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))

Siehe auch Wie man JSON-Daten in ein Python-Objekt umwandelt .

0 Stimmen

Scheint viel langsamer zu sein als die normale .loads wenn Sie ein großes JSON-Objekt haben

0 Stimmen

Das ist die Antwort. Danke, Kumpel.

34voto

Vanni Totaro Punkte 4594

Sie können sich die json Modul der Standardbibliothek mit einer benutzerdefinierter Objekthaken :

import json

class obj(object):
    def __init__(self, dict_):
        self.__dict__.update(dict_)

def dict2obj(d):
    return json.loads(json.dumps(d), object_hook=obj)

Beispiel für die Verwendung:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'bar'}]}
>>> o = dict2obj(d)
>>> o.a
1
>>> o.b.c
2
>>> o.d[0]
u'hi'
>>> o.d[1].foo
u'bar'

Und es ist nicht streng schreibgeschützt wie es bei namedtuple d.h. Sie können Werte ändern - nicht die Struktur:

>>> o.b.c = 3
>>> o.b.c
3

2 Stimmen

Die mit Abstand beste Antwort

0 Stimmen

Mir gefällt die Idee, den json-Lademechanismus für verschachtelte Elemente zu verwenden. Aber wir haben bereits ein Diktat und ich mag die Tatsache nicht, dass man daraus einen String erstellen muss, um ihn in ein Objekt zu mappen. Ich hätte lieber eine Lösung, die Objekte direkt aus einem Diktat erzeugt.

1 Stimmen

Muss zuerst in einen String konvertiert werden, ist aber ziemlich generisch, da man es über json.JSONEncoder y object_hook .

31voto

andyvanee Punkte 958

Ich habe die meiner Meinung nach besten Aspekte der vorangegangenen Beispiele genommen und mir folgendes ausgedacht:

class Struct:
  '''The recursive class for building and representing objects with.'''
  def __init__(self, obj):
    for k, v in obj.iteritems():
      if isinstance(v, dict):
        setattr(self, k, Struct(v))
      else:
        setattr(self, k, v)
  def __getitem__(self, val):
    return self.__dict__[val]
  def __repr__(self):
    return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for
      (k, v) in self.__dict__.iteritems()))

0 Stimmen

Beachten Sie, dass der Konstruktor zu verkürzt werden kann: def __init__(self, dct): for k, v in dct.iteritems(): setattr(self, k, isinstance(v, dict) and self.__class__(v) or v) wodurch auch der explizite Aufruf von Struct

4 Stimmen

Ich möchte lieber nicht meine eigene Antwort abwerten, aber im Nachhinein habe ich festgestellt, dass es nicht in Sequenztypen rekursiert. x.d[1].foo schlägt in diesem Fall fehl.

2 Stimmen

Die isinstance(v, dict) Kontrolle wäre besser als isinstance(v, collections.Mapping) damit es in Zukunft mit diktatähnlichen Dingen umgehen kann

22voto

JayD3e Punkte 2057

Am Ende habe ich beides ausprobiert AttrDict und die Bündel Bibliotheken und fand sie für meine Zwecke viel zu langsam. Nachdem ein Freund und ich uns damit befasst hatten, fanden wir heraus, dass die Hauptmethode zum Schreiben dieser Bibliotheken dazu führt, dass die Bibliothek aggressiv durch ein verschachteltes Objekt rekursiert und währenddessen Kopien des Wörterbuchobjekts erstellt. Vor diesem Hintergrund haben wir zwei wesentliche Änderungen vorgenommen. 1) Wir haben die Attribute "lazy-loaded" gemacht. 2) Anstatt Kopien eines Wörterbuchobjekts zu erstellen, erstellen wir Kopien eines leichtgewichtigen Proxy-Objekts. Dies ist die endgültige Implementierung. Die Leistungssteigerung durch die Verwendung dieses Codes ist unglaublich. Bei der Verwendung von AttrDict oder Bunch verbrauchten diese beiden Bibliotheken allein 1/2 bzw. 1/3 meiner Anfragezeit (was!?). Dieser Code hat diese Zeit auf fast nichts reduziert (irgendwo im Bereich von 0,5 ms). Dies hängt natürlich von Ihren Bedürfnissen ab, aber wenn Sie diese Funktionalität in Ihrem Code häufig verwenden, sollten Sie auf jeden Fall etwas Einfaches wie dieses 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

Siehe die ursprüngliche Implementierung ici von https://stackoverflow.com/users/704327/michael-merickel .

Außerdem ist zu beachten, 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.

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