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?

Marc
quelle
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
Eli Bendersky
quelle
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'
Nadia Alramli
quelle
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

Kontinuität
quelle
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'
SilentGhost
quelle
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
Umbrae
quelle
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.

XEye
quelle
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()))
andyvanee
quelle
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 .

DS.
quelle
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'}}
user1783597
quelle
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'
Anon
quelle
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.

JayD3e
quelle
9

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

Alex Rodrigues
quelle
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
Vanni Totaro
quelle
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.

Aaron Digulla
quelle
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
Aidan Haddon-Wright
quelle
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
nach vorne
quelle
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"
Dawie Strauss
quelle
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()
truease.com
quelle
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!

Erik
quelle
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
naren
quelle
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
rauben
quelle
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.

NiKo
quelle
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"
Kochfelder
quelle
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.

Brian
quelle
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
Paulo Freitas
quelle
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
onno
quelle
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': ''}
tcpiper
quelle
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']

VengaVenga
quelle
1

Mein Wörterbuch hat dieses Format:

addr_bk = {
    'person': [
        {'name': 'Andrew', 'id': 123, 'email': '[email protected]',
         'phone': [{'type': 2, 'number': '633311122'},
                   {'type': 0, 'number': '97788665'}]
        },
        {'name': 'Tom', 'id': 456,
         'phone': [{'type': 0, 'number': '91122334'}]}, 
        {'name': 'Jack', 'id': 7788, 'email': '[email protected]'}
    ]
}

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
Whospal
quelle
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 .
David X.
quelle