Verschachteltes Python-Diktat in Objekt konvertieren?


538

Ich suche nach einer eleganten Möglichkeit, Daten mithilfe des Attributzugriffs auf ein Diktat mit einigen verschachtelten Diktaten und Listen abzurufen (dh Objektsyntax im Javascript-Stil).

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 ohne Rekursion nicht möglich, aber was wäre ein guter Weg, um einen Objektstil für Diktate zu erhalten?


6
Ich habe kürzlich versucht, etwas Ähnliches zu tun, aber ein wiederkehrender Wörterbuchschlüssel ("from" - ein Python-Schlüsselwort) hat mich daran gehindert, damit fertig zu werden. Denn sobald Sie versucht haben, mit "x.from" auf dieses Attribut zuzugreifen, wird ein Syntaxfehler angezeigt.
Dawie Strauss

3
Das ist in der Tat ein Problem, aber ich kann auf "von" verzichten, um den Zugriff auf große Diktatkonstrukte zu erleichtern :) Die Eingabe von x ['a'] ['d'] [1] ['foo'] ist wirklich ärgerlich, also xad [1] .foo Regeln. Wenn Sie von benötigen, können Sie über getattr (x, 'from') darauf zugreifen oder stattdessen _from als Attribut verwenden.
Marc

5
from_eher als _fromnach PEP 8 .
Kos

1
Sie können getattr(x, 'from')das Attribut verwenden, anstatt es umzubenennen.
George V. Reilly

3
Die meisten dieser "Lösungen" scheinen nicht zu funktionieren (selbst die akzeptierte, erlaubt keine Verschachtelung d1.b.c). Ich denke, es ist klar, dass Sie etwas aus einer Bibliothek verwenden sollten, z. B. ein benanntes Tupel aus Sammlungen , wie diese Antwort nahelegt. .
Andy Hayden

Antworten:


660

Update: Überlegen Sie in Python 2.6 und höher, ob die namedtupleDatenstruktur Ihren Anforderungen 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 Antwortinhalt) 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

19
Gleiches gilt hier - dies ist besonders nützlich für die Rekonstruktion von Python-Objekten aus dokumentenorientierten Datenbanken wie MongoDB.
Mikemaccana

13
Um einen schöneren Druck zu erhalten, fügen Sie Folgendes hinzu: def repr __ (self): Geben Sie '<% s>'% str ('\ n' .join ('% s:% s'% (k, repr (v)) für (k, v) zurück ) in self .__ dict .iteritems ()))
six8

15
Funktioniert dies mit verschachtelten Wörterbüchern? und Diktate mit Objekten und / oder Listen usw. Gibt es irgendwelche Fänge?
Sam Stoelinga

5
@Sam S: Es werden keine verschachtelten Structs aus verschachtelten Wörterbüchern erstellt, aber im Allgemeinen kann der Werttyp beliebig sein. Der Schlüssel ist darauf beschränkt, für einen Objektschlitz geeignet zu sein
Eli Bendersky

51
-1 , weil a) es funktioniert nicht für verschachtelte dicts, die die Frage nach klar gefragt, und b) die gleiche Funktionalität existiert derzeit in Standardbibliotheken in argparse.Namespace(die auch definiert hat __eq__, __ne__, __contains__).
wim

110
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
Gut! Ich würde jedoch .items () durch .iteritems () ersetzen, um einen geringeren Speicherbedarf zu erzielen.
Eric O Lebigot

7
Wenn dies keine OP-Anforderung ist, ist dies kein Problem. Beachten Sie jedoch, dass Objekte in Listen innerhalb von Listen nicht rekursiv verarbeitet werden.
Anon

3
Mir ist klar, dass dies eine alte Antwort ist, aber heutzutage wäre es besser, eine abstrakte Basisklasse anstelle dieser hässlichen Linie zu verwendenif isinstance(b, (list, tuple)):
wim

4
Tipp: Lassen Sie die objKlasse argparse.Namespacefür zusätzliche Funktionen wie die lesbare Zeichenfolgendarstellung erben .
Serrano

2
Abhängig vom Anwendungsfall würden wir normalerweise überprüfen, ob es sich nicht um einen Zeichenfolgentyp handelt, sondern um einen
Sequenztyp

108

Überraschenderweise hat niemand Bunch erwähnt . Diese Bibliothek soll ausschließlich den Zugriff auf Attributstile auf diktierte Objekte ermöglichen und genau das tun, was das OP wünscht. 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 finden Sie unter https://github.com/Infinidat/munch - Das Guthaben geht an codyzu


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

Bunch ist die beste Lösung von all diesen, da es mehrere Arten der Serialisierung gut unterstützt und beibehalten wird. Grosses Dankeschön. Ich wünschte, die Python3-Version hätte den gleichen Namen, weil warum nicht.
Jonathan

4
Nur eine Vorwarnung, Bunch und Attrdict sind also auch sehr langsam. Sie verbrauchten ungefähr 1/3 bzw. 1/2 meiner Anforderungszeit, als sie in unserer Anwendung häufig verwendet wurden. Auf keinen Fall etwas zu ignorieren. Sprechen Sie mehr darüber stackoverflow.com/a/31569634/364604 .
JayD3e

Dies ermöglicht zwar einen objektähnlichen Zugriff auf Diktate, jedoch über getattr . Dies erschwert die Selbstbeobachtung in intelligenten REPLs wie IPython.
GDorn

Neugierig, wie dies im Vergleich zu JSONpath github.com/kennknowles/python-jsonpath-rw
MarkHu

64
x = type('new_dict', (object,), d)

Fügen Sie dann eine Rekursion hinzu und Sie sind fertig.

Bearbeiten Sie dies so, wie ich es implementieren würde:

>>> 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
Warum erstellen Sie Typobjekte und instanziieren sie nicht? Wäre das nicht logischer? Ich meine, warum nicht tun top_instance = top()und das zurückgeben, wo Sie zurückkehren top?
Pfannkuchen

6
Schön für die "Blatt" xx.b<class '__main__.new'>
-Daten

46

Es gibt einen Sammlungshelfer namens namedtuple, der dies für Sie tun kann:

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

28
Dies beantwortet nicht die Frage der Rekursion für die verschachtelten Diktate.
abzugsfähig

4
Sie können namedtuples nicht ändern.
Max

Können wir garantieren, dass die Reihenfolge der von keys () und values ​​() zurückgegebenen Liste in der Reihenfolge übereinstimmt? Ich meine, wenn es ein OrderedDict ist, ja. Aber ein Standarddiktat? Ich weiß, dass CPython 3.6+ und PyPy "kompakte" Diktate bestellt werden, zitiere jedoch die Dokumentation: "Der auftragserhaltende Aspekt dieser neuen Implementierung wird als Implementierungsdetail betrachtet und sollte nicht als verlässlich angesehen werden"
Havok,

38
class Struct(object):
    """Comment removed"""
    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 / Wertestruktur beliebiger Tiefe verwendet werden.


4
Dies sollte die Antwort sein. Es funktioniert gut zum Verschachteln. Sie können dies auch als object_hook für json.load () verwenden.
010110110101

2
Ähnlich wie bei SilentGhosts funktionaler Antwort aus dem Jahr 2009 sind die Blattknotendaten zugänglich, aber die Eltern / Zweige werden als Objektreferenzen angezeigt. Zum hübschen Druckendef __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
MarkHu

5
Python 3.x-Benutzer: Es ist nur .items()statt .iteritems()in Zeile 4. (Die Funktion wurde umbenannt, tut aber im Wesentlichen das gleiche )
neo post modern

Funktioniert perfekt für verschachtelte Objekte, danke! Als Kommentar müssen Neulinge wie ich für python3 iteritems () in items () geändert werden
Óscar Andreu

31

Ich nehme an, was die besten Aspekte der vorherigen Beispiele sind, und habe 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()))

Beachten Sie, dass der Konstruktor verkürzt werden kann auf: 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 vonStruct
George V. Reilly

2
Ich würde meine eigene Antwort lieber nicht ablehnen, aber wenn ich zurückblicke, habe ich festgestellt, dass sie nicht in Sequenztypen zurückkehrt. xd [1] .foo schlägt in diesem Fall fehl.
Andyvanee

2
Der isinstance(v, dict)Scheck wäre besser, da isinstance(v, collections.Mapping)er zukünftige diktähnliche Dinge handhaben kann
Kochfelder

29

Wenn Ihr Diktat stammt json.loads(), können Sie es stattdessen in einer Zeile in ein Objekt (anstatt 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 So konvertieren Sie JSON-Daten in ein Python-Objekt .


Scheint viel langsamer als normal zu sein, .loadswenn Sie ein riesiges JSON-Objekt haben
michaelsnowden

16

Wenn Sie als Objekt (oder als Diktat für schwierige Schlüssel) auf Diktatschlüssel zugreifen möchten, tun Sie dies rekursiv und können Sie auch das ursprüngliche Diktat aktualisieren. Sie können Folgendes tun:

class Dictate(object):
    """Object view of a dict, updating the passed in dict when values are set
    or deleted. "Dictate" the contents of a dict...: """

    def __init__(self, d):
        # since __setattr__ is overridden, self.__dict = d doesn't work
        object.__setattr__(self, '_Dictate__dict', d)

    # Dictionary-like access / updates
    def __getitem__(self, name):
        value = self.__dict[name]
        if isinstance(value, dict):  # recursively view sub-dicts as objects
            value = Dictate(value)
        return value

    def __setitem__(self, name, value):
        self.__dict[name] = value
    def __delitem__(self, name):
        del self.__dict[name]

    # Object-like access / updates
    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        self[name] = value
    def __delattr__(self, name):
        del self[name]

    def __repr__(self):
        return "%s(%r)" % (type(self).__name__, self.__dict)
    def __str__(self):
        return str(self.__dict)

Anwendungsbeispiel:

d = {'a': 'b', 1: 2}
dd = Dictate(d)
assert dd.a == 'b'  # Access like an object
assert dd[1] == 2  # Access like a dict
# Updates affect d
dd.c = 'd'
assert d['c'] == 'd'
del dd.a
del dd[1]
# Inner dicts are mapped
dd.e = {}
dd.e.f = 'g'
assert dd['e'].f == 'g'
assert d == {'c': 'd', 'e': {'f': 'g'}}

13
>>> def dict2obj(d):
        if isinstance(d, list):
            d = [dict2obj(x) for x in d]
        if not isinstance(d, dict):
            return d
        class C(object):
            pass
        o = C()
        for k in d:
            o.__dict__[k] = dict2obj(d[k])
        return o


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

12

Am Ende habe ich BEIDE AttrDict ausprobiert als auch das BunchBibliotheken 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 beansprucht (was!?). Dieser Code reduzierte diese Zeit auf fast nichts (irgendwo im Bereich von 0,5 ms). Dies hängt natürlich von Ihren Bedürfnissen ab,

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.


9

x.__dict__.update(d) sollte gut tun.


danke für deine antwort, aber was ist x? das Diktat oder ein Standardobjekt? Könnten Sie mir bitte einen Hinweis geben?
Marc

x ist dein Objekt. Jedes Objekt hat ein Diktat . Durch Aktualisieren des Diktats eines Objekts aktualisieren Sie tatsächlich die darin enthaltenen Schlüsselvariablen.
Alex Rodrigues

Die kühnen Worte sind _ _ dict _ _
Alex Rodrigues

15
Dies behandelt keine verschachtelten Wörterbücher.
FogleBird

Nicht jedes Objekt hat einen __dict__: Versuch object().__dict__und du wirstAttributeError: 'object' object has no attribute '__dict__'
Will Manley

8

Sie können das jsonModul der Standardbibliothek mit einem benutzerdefinierten Objekt-Hook nutzen :

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)

Anwendungsbeispiel:

>>> 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 bei namedtuple, dh Sie können Werte ändern - nicht Struktur:

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

1
Beste Antwort bei weitem
Connor

Ich mag 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 einem Objekt zuzuordnen. Ich hätte lieber eine Lösung, die Objekte direkt aus einem Diktat erstellt.
Sandro

1
Muss zuerst in einen String konvertiert werden, ist aber ziemlich allgemein gehalten, da man ihn über json.JSONEncoderund erweitern kann object_hook.
Sandro

6

Damit sollten Sie loslegen:

class dict2obj(object):
    def __init__(self, d):
        self.__dict__['d'] = d

    def __getattr__(self, key):
        value = self.__dict__['d'][key]
        if type(value) == type({}):
            return dict2obj(value)

        return value

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

x = dict2obj(d)
print x.a
print x.b.c
print x.d[1].foo

Es funktioniert noch nicht für Listen. Sie müssen die Listen in eine Benutzerliste einbinden und überladen __getitem__, um Diktate zu verpacken.


2
Verwenden Sie die if isinstance(d, list)Klausel aus Anonder Antwort von , damit es für Listen funktioniert .
Vinay Sajip

6

Ich weiß, dass es hier bereits viele Antworten gibt und ich bin zu spät zur Party, aber diese Methode konvertiert rekursiv und 'an Ort und Stelle' ein Wörterbuch in eine objektähnliche Struktur ... Funktioniert in 3.xx.

def dictToObject(d):
    for k,v in d.items():
        if isinstance(v, dict):
            d[k] = dictToObject(v)
    return namedtuple('object', d.keys())(*d.values())

# Dictionary created from JSON file
d = {
    'primaryKey': 'id', 
    'metadata': 
        {
            'rows': 0, 
            'lastID': 0
        }, 
    'columns': 
        {
            'col2': {
                'dataType': 'string', 
                'name': 'addressLine1'
            }, 
            'col1': {
                'datatype': 'string', 
                'name': 'postcode'
            }, 
            'col3': {
                'dataType': 'string', 
                'name': 'addressLine2'
            }, 
            'col0': {
                'datatype': 'integer', 
                'name': 'id'
            }, 
            'col4': {
                'dataType': 'string', 
                'name': 'contactNumber'
            }
        }, 
        'secondaryKeys': {}
}

d1 = dictToObject(d)
d1.columns.col1 # == object(datatype='string', name='postcode')
d1.metadata.rows # == 0

Was meinst du mit "objektartiger Struktur"?
MoralCode

1
Objektartig wegen des Ententypisierungsprinzips. Ich meine, Sie können auf seine Eigenschaften zugreifen, als wären Sie eine Instanz einer Klasse. aber es ist KEIN Objekt in diesem Sinne, es ist ein benanntes Tupel.
Aidan Haddon-Wright

5
from mock import Mock
d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
my_data = Mock(**d)

# We got
# my_data.a == 1

4

Lassen Sie mich eine Lösung erklären, die ich vor einiger Zeit fast verwendet habe. Aber zuerst wird der Grund, warum ich es nicht getan habe, durch die Tatsache veranschaulicht, dass der folgende Code:

d = {'from': 1}
x = dict2obj(d)

print x.from

gibt diesen Fehler:

  File "test.py", line 20
    print x.from == 1
                ^
SyntaxError: invalid syntax

Da "from" ein Python-Schlüsselwort ist, gibt es bestimmte Wörterbuchschlüssel, die Sie nicht zulassen können.


Jetzt ermöglicht meine Lösung den Zugriff auf die Wörterbuchelemente, indem deren Namen direkt verwendet werden. Sie können aber auch die "Wörterbuchsemantik" verwenden. Hier ist der Code mit Beispielverwendung:

class dict2obj(dict):
    def __init__(self, dict_):
        super(dict2obj, self).__init__(dict_)
        for key in self:
            item = self[key]
            if isinstance(item, list):
                for idx, it in enumerate(item):
                    if isinstance(it, dict):
                        item[idx] = dict2obj(it)
            elif isinstance(item, dict):
                self[key] = dict2obj(item)

    def __getattr__(self, key):
        return self[key]

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

x = dict2obj(d)

assert x.a == x['a'] == 1
assert x.b.c == x['b']['c'] == 2
assert x.d[1].foo == x['d'][1]['foo'] == "bar"

1
Klasse Struktur: def init __ (self, ** Einträge): self .__ dict .update (Einträge)
Kenneth Reitz

4

Alte Fragen und Antworten, aber ich bekomme noch etwas zu reden. Es scheint, als würde niemand über rekursives Diktat sprechen. Das ist mein Code:

#!/usr/bin/env python

class Object( dict ):
    def __init__( self, data = None ):
        super( Object, self ).__init__()
        if data:
            self.__update( data, {} )

    def __update( self, data, did ):
        dataid = id(data)
        did[ dataid ] = self

        for k in data:
            dkid = id(data[k])
            if did.has_key(dkid):
                self[k] = did[dkid]
            elif isinstance( data[k], Object ):
                self[k] = data[k]
            elif isinstance( data[k], dict ):
                obj = Object()
                obj.__update( data[k], did )
                self[k] = obj
                obj = None
            else:
                self[k] = data[k]

    def __getattr__( self, key ):
        return self.get( key, None )

    def __setattr__( self, key, value ):
        if isinstance(value,dict):
            self[key] = Object( value )
        else:
            self[key] = value

    def update( self, *args ):
        for obj in args:
            for k in obj:
                if isinstance(obj[k],dict):
                    self[k] = Object( obj[k] )
                else:
                    self[k] = obj[k]
        return self

    def merge( self, *args ):
        for obj in args:
            for k in obj:
                if self.has_key(k):
                    if isinstance(self[k],list) and isinstance(obj[k],list):
                        self[k] += obj[k]
                    elif isinstance(self[k],list):
                        self[k].append( obj[k] )
                    elif isinstance(obj[k],list):
                        self[k] = [self[k]] + obj[k]
                    elif isinstance(self[k],Object) and isinstance(obj[k],Object):
                        self[k].merge( obj[k] )
                    elif isinstance(self[k],Object) and isinstance(obj[k],dict):
                        self[k].merge( obj[k] )
                    else:
                        self[k] = [ self[k], obj[k] ]
                else:
                    if isinstance(obj[k],dict):
                        self[k] = Object( obj[k] )
                    else:
                        self[k] = obj[k]
        return self

def test01():
    class UObject( Object ):
        pass
    obj = Object({1:2})
    d = {}
    d.update({
        "a": 1,
        "b": {
            "c": 2,
            "d": [ 3, 4, 5 ],
            "e": [ [6,7], (8,9) ],
            "self": d,
        },
        1: 10,
        "1": 11,
        "obj": obj,
    })
    x = UObject(d)


    assert x.a == x["a"] == 1
    assert x.b.c == x["b"]["c"] == 2
    assert x.b.d[0] == 3
    assert x.b.d[1] == 4
    assert x.b.e[0][0] == 6
    assert x.b.e[1][0] == 8
    assert x[1] == 10
    assert x["1"] == 11
    assert x[1] != x["1"]
    assert id(x) == id(x.b.self.b.self) == id(x.b.self)
    assert x.b.self.a == x.b.self.b.self.a == 1

    x.x = 12
    assert x.x == x["x"] == 12
    x.y = {"a":13,"b":[14,15]}
    assert x.y.a == 13
    assert x.y.b[0] == 14

def test02():
    x = Object({
        "a": {
            "b": 1,
            "c": [ 2, 3 ]
        },
        1: 6,
        2: [ 8, 9 ],
        3: 11,
    })
    y = Object({
        "a": {
            "b": 4,
            "c": [ 5 ]
        },
        1: 7,
        2: 10,
        3: [ 12 , 13 ],
    })
    z = {
        3: 14,
        2: 15,
        "a": {
            "b": 16,
            "c": 17,
        }
    }
    x.merge( y, z )
    assert 2 in x.a.c
    assert 3 in x.a.c
    assert 5 in x.a.c
    assert 1 in x.a.b
    assert 4 in x.a.b
    assert 8 in x[2]
    assert 9 in x[2]
    assert 10 in x[2]
    assert 11 in x[3]
    assert 12 in x[3]
    assert 13 in x[3]
    assert 14 in x[3]
    assert 15 in x[2]
    assert 16 in x.a.b
    assert 17 in x.a.c

if __name__ == '__main__':
    test01()
    test02()

4

Wollte meine Version dieses kleinen Paradigmas hochladen.

class Struct(dict):
  def __init__(self,data):
    for key, value in data.items():
      if isinstance(value, dict):
        setattr(self, key, Struct(value))
      else:   
        setattr(self, key, type(value).__init__(value))

      dict.__init__(self,data)

Die Attribute für den Typ, der in die Klasse importiert wird, bleiben erhalten. Mein einziges Anliegen wäre es, Methoden aus dem Wörterbuch zu überschreiben, die Sie analysieren. Aber sonst scheint solide!


Obwohl dies eine gute Idee ist, scheint dies für das Beispiel des OP nicht zu funktionieren, tatsächlich scheint es das übergebene Wörterbuch zu ändern! In der Tat df.afunktioniert nicht einmal.
Andy Hayden

4

Das funktioniert auch gut

class DObj(object):
    pass

dobj = Dobj()
dobj.__dict__ = {'a': 'aaa', 'b': 'bbb'}

print dobj.a
>>> aaa
print dobj.b
>>> bbb

3

Hier ist eine andere Möglichkeit, den ursprünglichen Vorschlag von SilentGhost umzusetzen:

def dict2obj(d):
  if isinstance(d, dict):
    n = {}
    for item in d:
      if isinstance(d[item], dict):
        n[item] = dict2obj(d[item])
      elif isinstance(d[item], (list, tuple)):
        n[item] = [dict2obj(elem) for elem in d[item]]
      else:
        n[item] = d[item]
    return type('obj_from_dict', (object,), n)
  else:
    return d

3

Ich stolperte über den Fall, dass ich eine Liste von Diktaten rekursiv in eine Liste von Objekten konvertieren musste. Basierend auf Robertos Ausschnitt hier, was hat die Arbeit für mich getan:

def dict2obj(d):
    if isinstance(d, dict):
        n = {}
        for item in d:
            if isinstance(d[item], dict):
                n[item] = dict2obj(d[item])
            elif isinstance(d[item], (list, tuple)):
                n[item] = [dict2obj(elem) for elem in d[item]]
            else:
                n[item] = d[item]
        return type('obj_from_dict', (object,), n)
    elif isinstance(d, (list, tuple,)):
        l = []
        for item in d:
            l.append(dict2obj(item))
        return l
    else:
        return d

Beachten Sie, dass jedes Tupel aus offensichtlichen Gründen in sein Listenäquivalent konvertiert wird.

Hoffe, das hilft jemandem genauso wie all deine Antworten für mich, Jungs.


3

Was die Zuordnung über nur Ihre dictzu den __dict__einem leeren Objekt?

class Object:
    """If your dict is "flat", this is a simple way to create an object from a dict

    >>> obj = Object()
    >>> obj.__dict__ = d
    >>> d.a
    1
    """
    pass

Dies schlägt natürlich bei Ihrem verschachtelten Diktatbeispiel fehl, es sei denn, Sie gehen das Diktat rekursiv durch:

# For a nested dict, you need to recursively update __dict__
def dict2obj(d):
    """Convert a dict to an object

    >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    >>> obj = dict2obj(d)
    >>> obj.b.c
    2
    >>> obj.d
    ["hi", {'foo': "bar"}]
    """
    try:
        d = dict(d)
    except (TypeError, ValueError):
        return d
    obj = Object()
    for k, v in d.iteritems():
        obj.__dict__[k] = dict2obj(v)
    return obj

Und Ihr Beispiellistenelement sollte wahrscheinlich Mappingeine Liste von (Schlüssel-, Wert-) Paaren wie folgt sein:

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

2

Hier ist eine weitere Implementierung:

class DictObj(object):
    def __init__(self, d):
        self.__dict__ = d

def dict_to_obj(d):
    if isinstance(d, (list, tuple)): return map(dict_to_obj, d)
    elif not isinstance(d, dict): return d
    return DictObj(dict((k, dict_to_obj(v)) for (k,v) in d.iteritems()))

[Bearbeiten] Es fehlte etwas, um auch Diktate in Listen zu behandeln, nicht nur andere Diktate. Fix hinzugefügt.


Beachten Sie, dass das Festlegen des Diktats für das Quellwörterbuch bedeutet, dass Änderungen an Attributen des resultierenden Objekts auch das Wörterbuch betreffen, mit dem das Objekt erstellt wurde, und umgekehrt. Dies kann zu unerwarteten Ergebnissen führen, wenn das Wörterbuch nicht für die Erstellung des Objekts verwendet wird.
Mark Roddy

@Mark: Tatsächlich wird jedes Mal ein neues Wörterbuch an DictObj übergeben, anstatt nur dasselbe Diktierobjekt zu durchlaufen, sodass dies nicht tatsächlich auftritt. Dies ist erforderlich, da ich die Werte auch innerhalb eines Wörterbuchs übersetzen muss , sodass es unmöglich ist, das ursprüngliche Diktierobjekt zu durchlaufen, ohne es selbst zu mutieren.
Brian

Es gibt einige Antworten darauf, die akzeptierte Antwort sah nicht so aus, als wäre sie rekursiv oder würde Listen verarbeiten. Ich habe sie alle durchgelesen und dieser sah auf den ersten Blick am einfachsten aus, ich habe ihn getestet und er hat funktioniert. Gute Antwort.
AlwaysTraining

2
class Struct(dict):
    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

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

    def copy(self):
        return Struct(dict.copy(self))

Verwendungszweck:

points = Struct(x=1, y=2)
# Changing
points['x'] = 2
points.y = 1
# Accessing
points['x'], points.x, points.get('x') # 2 2 2
points['y'], points.y, points.get('y') # 1 1 1
# Accessing inexistent keys/attrs 
points['z'] # KeyError: z
points.z # AttributeError: z
# Copying
points_copy = points.copy()
points.x = 2
points_copy.x # 1

2

Wie wäre es damit:

from functools import partial
d2o=partial(type, "d2o", ())

Dies kann dann folgendermaßen verwendet werden:

>>> o=d2o({"a" : 5, "b" : 3})
>>> print o.a
5
>>> print o.b
3

2

Ich denke, ein Diktat besteht aus Zahl, Zeichenfolge und Diktat reicht meistens aus. Daher ignoriere ich die Situation, dass Tupel, Listen und andere Typen nicht in der endgültigen Dimension eines Diktats erscheinen.

In Anbetracht der Vererbung in Kombination mit der Rekursion wird das Druckproblem bequem gelöst und es gibt zwei Möglichkeiten zum Abfragen von Daten, eine Möglichkeit zum Bearbeiten von Daten.

Sehen Sie sich das folgende Beispiel an, ein Diktat, das einige Informationen über Schüler beschreibt:

group=["class1","class2","class3","class4",]
rank=["rank1","rank2","rank3","rank4","rank5",]
data=["name","sex","height","weight","score"]

#build a dict based on the lists above
student_dic=dict([(g,dict([(r,dict([(d,'') for d in data])) for r in rank ]))for g in group])

#this is the solution
class dic2class(dict):
    def __init__(self, dic):
        for key,val in dic.items():
            self.__dict__[key]=self[key]=dic2class(val) if isinstance(val,dict) else val


student_class=dic2class(student_dic)

#one way to edit:
student_class.class1.rank1['sex']='male'
student_class.class1.rank1['name']='Nan Xiang'

#two ways to query:
print student_class.class1.rank1
print student_class.class1['rank1']
print '-'*50
for rank in student_class.class1:
    print getattr(student_class.class1,rank)

Ergebnisse:

{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
--------------------------------------------------
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}

2

Normalerweise möchten Sie die Diktathierarchie in Ihr Objekt spiegeln, jedoch keine Liste oder Tupel, die sich normalerweise auf der niedrigsten Ebene befinden. So habe ich das gemacht:

class defDictToObject(object):

    def __init__(self, myDict):
        for key, value in myDict.items():
            if type(value) == dict:
                setattr(self, key, defDictToObject(value))
            else:
                setattr(self, key, value)

So machen wir es:

myDict = { 'a': 1,
           'b': { 
              'b1': {'x': 1,
                    'y': 2} },
           'c': ['hi', 'bar'] 
         }

und bekomme:

x.b.b1.x 1

x.c ['hi', 'bar']


1

Mein Wörterbuch hat dieses Format:

addr_bk = {
    'person': [
        {'name': 'Andrew', 'id': 123, 'email': 'andrew@mailserver.com',
         'phone': [{'type': 2, 'number': '633311122'},
                   {'type': 0, 'number': '97788665'}]
        },
        {'name': 'Tom', 'id': 456,
         'phone': [{'type': 0, 'number': '91122334'}]}, 
        {'name': 'Jack', 'id': 7788, 'email': 'jack@gmail.com'}
    ]
}

Wie zu sehen ist, habe ich Wörterbücher und eine Liste von Diktaten verschachtelt . Dies liegt daran, dass addr_bk aus Protokollpufferdaten dekodiert wurde, die mit lwpb.codec in ein Python-Dikt konvertiert wurden. Es gibt ein optionales Feld (z. B. E-Mail => wo der Schlüssel möglicherweise nicht verfügbar ist) und ein wiederholtes Feld (z. B. Telefon => in Diktatliste konvertiert).

Ich habe alle oben vorgeschlagenen Lösungen ausprobiert. Einige verarbeiten die verschachtelten Wörterbücher nicht gut. Andere können die Objektdetails nicht einfach drucken.

Nur die Lösung dict2obj (dict) von Dawie Strauss funktioniert am besten.

Ich habe es ein wenig verbessert, um damit umzugehen, wenn der Schlüssel nicht gefunden werden kann:

# Work the best, with nested dictionaries & lists! :)
# Able to print out all items.
class dict2obj_new(dict):
    def __init__(self, dict_):
        super(dict2obj_new, self).__init__(dict_)
        for key in self:
            item = self[key]
            if isinstance(item, list):
                for idx, it in enumerate(item):
                    if isinstance(it, dict):
                        item[idx] = dict2obj_new(it)
            elif isinstance(item, dict):
                self[key] = dict2obj_new(item)

    def __getattr__(self, key):
        # Enhanced to handle key not found.
        if self.has_key(key):
            return self[key]
        else:
            return None

Dann habe ich es getestet mit:

# Testing...
ab = dict2obj_new(addr_bk)

for person in ab.person:
  print "Person ID:", person.id
  print "  Name:", person.name
  # Check if optional field is available before printing.
  if person.email:
    print "  E-mail address:", person.email

  # Check if optional field is available before printing.
  if person.phone:
    for phone_number in person.phone:
      if phone_number.type == codec.enums.PhoneType.MOBILE:
        print "  Mobile phone #:",
      elif phone_number.type == codec.enums.PhoneType.HOME:
        print "  Home phone #:",
      else:
        print "  Work phone #:",
      print phone_number.number

1

Aufbauend auf meiner Antwort auf " Python: Wie füge ich einer Klasse dynamisch Eigenschaften hinzu? ":

class data(object):
    def __init__(self,*args,**argd):
        self.__dict__.update(dict(*args,**argd))

def makedata(d):
    d2 = {}
    for n in d:
        d2[n] = trydata(d[n])
    return data(d2)

def trydata(o):
    if isinstance(o,dict):
        return makedata(o)
    elif isinstance(o,list):
        return [trydata(i) for i in o]
    else:
        return o

Sie rufen makedatadas Wörterbuch auf, das Sie konvertieren möchten, oder trydataabhängig davon, was Sie als Eingabe erwarten, und es spuckt ein Datenobjekt aus.

Anmerkungen:

  • Sie können elifs hinzufügen, trydatawenn Sie mehr Funktionen benötigen.
  • Offensichtlich funktioniert dies nicht, wenn Sie wollen x.a = {}oder ähnliches.
  • Wenn Sie eine schreibgeschützte Version wünschen, verwenden Sie die Klassendaten aus der ursprünglichen Antwort .
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.