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 .

742voto

Eli Bendersky Punkte 246100

Aktualisierung: In Python 2.6 und höher ist zu prüfen, ob die namedtuple Datenstruktur Ihren Bedürfnissen entspricht:

>>> from collections import namedtuple
>>> MyStruct = namedtuple('MyStruct', 'a b d')
>>> s = MyStruct(a=1, b={'c': 2}, d=['hi'])
>>> s
MyStruct(a=1, b={'c': 2}, d=['hi'])
>>> s.a
1
>>> s.b
{'c': 2}
>>> s.c
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyStruct' object has no attribute 'c'
>>> s.d
['hi']

Die Alternative (ursprünglicher Inhalt der Antwort) ist:

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

Dann können Sie verwenden:

>>> args = {'a': 1, 'b': 2}
>>> s = Struct(**args)
>>> s
<__main__.Struct instance at 0x01D6A738>
>>> s.a
1
>>> s.b
2

23 Stimmen

Dies ist besonders nützlich, um Python-Objekte aus dokumentenorientierten Datenbanken wie MongoDB zu rekonstruieren.

16 Stimmen

Um einen schöneren Druck zu erhalten, fügen Sie hinzu: def repr__(self): return '<%s>' % str(' \n '.join('%s : %s' % (k, repr(v)) for (k, v) in self.__dict .iteritems()))

17 Stimmen

Funktioniert das auch mit verschachtelten Dictionaries? und Dicts, die Objekte oder Listen usw. enthalten? Gibt es irgendwelche Hindernisse?

170voto

kontinuity Punkte 24917

Überraschenderweise hat niemand erwähnt Bündel . Diese Bibliothek ist ausschließlich für den Zugriff auf Diktatobjekte im Stil von Attributen gedacht und tut genau das, was der OP will. Eine Demonstration:

>>> from bunch import bunchify
>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> x = bunchify(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
'bar'

Eine Python 3-Bibliothek ist verfügbar unter https://github.com/Infinidat/munch - Die Anerkennung geht an codyzu

>>> from munch import DefaultMunch
>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> obj = DefaultMunch.fromDict(d)
>>> obj.b.c
2
>>> obj.a
1
>>> obj.d[1].foo
'bar'

25 Stimmen

Und es gibt einen Python 3 kompatiblen (scheint 2.6-3.4 zu sein) Zweig von Bunch namens Munch: github.com/Infinidat/munch

1 Stimmen

Bunch ist die beste Lösung von all diesen, weil es mehrere Arten der Serialisierung gut unterstützt und gepflegt wird. Großartig, vielen Dank. Ich wünschte, die Python3-Version hätte den gleichen Namen, denn warum nicht.

7 Stimmen

Also nur eine Vorwarnung, Bunch und Attrdict sind ebenfalls sehr langsam. Sie verbrauchten etwa 1/3 bzw. 1/2 meiner Anfragezeit, als sie in unserer Anwendung häufig verwendet wurden. Definitiv nichts, was man ignorieren sollte. Mehr darüber erzählen stackoverflow.com/a/31569634/364604 .

140voto

Nadia Alramli Punkte 105256
class obj(object):
    def __init__(self, d):
        for a, b in d.items():
            if isinstance(b, (list, tuple)):
               setattr(self, a, [obj(x) if isinstance(x, dict) else x for x in b])
            else:
               setattr(self, a, obj(b) if isinstance(b, dict) else b)

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> x = obj(d)
>>> x.b.c
2
>>> x.d[1].foo
'bar'

7 Stimmen

Gut! Ich würde allerdings .items() durch .iteritems() ersetzen, um den Speicherbedarf zu verringern.

7 Stimmen

Wenn es sich nicht um eine OP-Anforderung handelt, ist dies kein Problem - aber beachten Sie, dass dies keine rekursive Verarbeitung von Objekten in Listen innerhalb von Listen ermöglicht.

0 Stimmen

Schön! Aber dann muss das Diktat vollständig sein, wenn Sie die Klasse erstellen... Ich denke, das funktioniert... man könnte eine weitere Instanz erstellen, wenn man das Diktat ändert. Gut gemacht!

74voto

SilentGhost Punkte 285785
x = type('new_dict', (object,), d)

dann fügen Sie eine Rekursion hinzu und fertig.

bearbeiten So würde ich es umsetzen:

>>> d
{'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'bar'}]}
>>> def obj_dic(d):
    top = type('new', (object,), d)
    seqs = tuple, list, set, frozenset
    for i, j in d.items():
        if isinstance(j, dict):
            setattr(top, i, obj_dic(j))
        elif isinstance(j, seqs):
            setattr(top, i, 
                type(j)(obj_dic(sj) if isinstance(sj, dict) else sj for sj in j))
        else:
            setattr(top, i, j)
    return top

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

1 Stimmen

Warum erstellen Sie Typ-Objekte und instanziieren sie nicht? Wäre das nicht logischer? Ich meine, warum nicht top_instance = top() und die Rückgabe, bei der Sie top ?

6 Stimmen

Schön für die "Blatt"-Daten, aber die Beispiele lassen bequemerweise "Zweige" aus wie x y x.b die hässliche <class '__main__.new'>

66voto

XEye Punkte 711
# Applies to Python-3 Standard Library
class Struct(object):
    def __init__(self, data):
        for name, value in data.items():
            setattr(self, name, self._wrap(value))

    def _wrap(self, value):
        if isinstance(value, (tuple, list, set, frozenset)): 
            return type(value)([self._wrap(v) for v in value])
        else:
            return Struct(value) if isinstance(value, dict) else value

# Applies to Python-2 Standard Library
class Struct(object):
    def __init__(self, data):
        for name, value in data.iteritems():
            setattr(self, name, self._wrap(value))

    def _wrap(self, value):
        if isinstance(value, (tuple, list, set, frozenset)): 
            return type(value)([self._wrap(v) for v in value])
        else:
            return Struct(value) if isinstance(value, dict) else value

Kann mit jeder Sequenz/Diktat/Wert-Struktur beliebiger Tiefe verwendet werden.

6 Stimmen

Das sollte die Antwort sein. Es funktioniert gut für die Verschachtelung. Sie können dies als object_hook für json.load() als gut verwenden.

8 Stimmen

Ähnlich wie bei der funktionalen Antwort von SilentGhost aus dem Jahr 2009 - die Daten der Blattknoten sind zugänglich, aber die übergeordneten Knoten/Zweige werden als Objektreferenzen angezeigt. Zum Hübsch-drucken, def __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))

9 Stimmen

Python 3.x-Benutzer: Es ist nur .items() anstelle von .iteritems() in Zeile 4. (Die Funktion wurde umbenannt, hat aber im Wesentlichen dasselbe )

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