Convertire nict di Python nidificato in oggetto?


539

Sto cercando un modo elegante per ottenere dati utilizzando l'accesso agli attributi su un dict con alcuni elenchi e elenchi nidificati (ovvero sintassi di oggetti in stile javascript).

Per esempio:

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

Dovrebbe essere accessibile in questo modo:

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

Penso che questo non sia possibile senza ricorsione, ma quale sarebbe un bel modo per ottenere uno stile di oggetto per i dadi?


6
Di recente stavo provando a fare qualcosa di simile, ma una chiave del dizionario ricorrente ("from" - che è una parola chiave Python) mi ha impedito di farlo. Perché non appena si tenta di utilizzare "x.from" per accedere a quell'attributo si ottiene un errore di sintassi.
Dawie Strauss,

3
questo è davvero un problema, ma posso abbandonare "from" per rendere la vita più facile accedendo a grandi costrutti dict :) digitare x ['a'] ['d'] [1] ['foo'] è davvero fastidioso, quindi xad [1] .foo regole. se necessario, puoi accedervi tramite getattr (x, 'from') o utilizzare invece _from come attributo.
Marc,

5
from_piuttosto che _fromsecondo PEP 8 .
Kos

1
È possibile utilizzare getattr(x, 'from')invece di rinominare l'attributo.
George V. Reilly,

3
La maggior parte di queste "soluzioni" non sembrano funzionare (anche quella accettata, non consente l'annidamento d1.b.c), penso che sia chiaro che dovresti usare qualcosa da una libreria, ad esempio namedtuple dalle raccolte , come suggerisce questa risposta , .. .
Andy Hayden

Risposte:


660

Aggiornamento: in Python 2.6 e versioni successive, considera se la namedtuplestruttura dei dati soddisfa le tue esigenze:

>>> 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']

L'alternativa (contenuto della risposta originale) è:

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

Quindi, puoi usare:

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

19
Lo stesso qui - questo è particolarmente utile per ricostruire oggetti Python da database orientati ai documenti come MongoDB.
mikemaccana,

13
Per ottenere una stampa più carina aggiungi: def repr __ (self): restituisce '<% s>'% str ('\ n' .join ('% s:% s'% (k, repr (v)) per (k, v ) in self .__ dict .iteritems ()))
six8

16
Funzionerà con dizionari nidificati? e dadi contenenti oggetti e / o elenco ecc. Ci sono delle catture?
Sam Stoelinga,

5
@ Sam S: non creerà Structs nidificati da dizionari nidificati, ma in generale il tipo di valore può essere qualsiasi cosa. La chiave è limitata per essere adatta per uno slot per oggetti
Eli Bendersky,

52
-1 perché a) non funziona per dicts nidificati, che la questione chiaramente chiesto e b) la stessa funzionalità esiste attualmente in librerie standard in argparse.Namespace(che ha anche definito __eq__, __ne__, __contains__).
mercoledì

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
Buona! Sostituirei .items () con .iteritems (), tuttavia, per un footprint di memoria più piccolo.
Eric O Lebigot,

7
Se non è un requisito OP, questo non è un problema, ma tieni presente che non elaborerà in modo ricorsivo gli oggetti negli elenchi all'interno degli elenchi.
Anon,

3
Mi rendo conto che questa è una vecchia risposta, ma al giorno d'oggi sarebbe meglio usare una classe base astratta invece di quella brutta lineaif isinstance(b, (list, tuple)):
wim

4
Suggerimento: objè necessario ereditare la classe argparse.Namespaceper funzionalità aggiuntive come la rappresentazione di stringhe leggibili.
Serrano,

2
A seconda del caso d'uso, di solito abbiamo fatto il check che si tratta non è un tipo stringa, ma che è un tipo di sequenza
Wim

108

Sorprendentemente nessuno ha menzionato Bunch . Questa libreria ha il solo scopo di fornire accesso in stile attributo agli oggetti dict e fa esattamente ciò che l'OP vuole. Una dimostrazione:

>>> 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'

Una libreria Python 3 è disponibile su https://github.com/Infinidat/munch - Il credito va a codyzu


16
E c'è un ramo compatibile di Python 3 (sembra essere 2.6-3.4) di Bunch chiamato Munch: github.com/Infinidat/munch
codyzu

Bunch è la migliore soluzione di tutti questi perché supporta bene più tipi di serializzazione ed è mantenuta. Ottimo grazie. Vorrei che la versione di python3 fosse chiamata la stessa cosa, perché perché no.
Jonathan,

4
Quindi solo un avvertimento, anche Bunch e Attrdict sono molto lenti. Hanno consumato circa 1/3 e 1/2 del mio tempo di richiesta rispettivamente quando hanno visto un uso frequente nella nostra applicazione. Sicuramente non qualcosa da ignorare. Parlane di più stackoverflow.com/a/31569634/364604 .
JayD3e

Sebbene ciò consenta l'accesso simile a oggetti ai dicts, lo fa tramite getattr . Ciò rende difficile l'introspezione di REPL intelligenti come IPython.
GDorn

Curioso come questo si confronta con JSONpath github.com/kennknowles/python-jsonpath-rw
MarkHu

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

quindi aggiungi ricorsione a questo e il gioco è fatto.

modifica questo è come lo implementerei:

>>> 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
Perché stai creando oggetti tipo e non li crei un'istanza? Non sarebbe più logico? Voglio dire, perché non farlo top_instance = top()e restituirlo al tuo ritorno top?
Pancake

6
Bello per i dati "foglia", ma gli esempi tralasciano convenientemente "ramoscelli" simili xe x.bche ritornano brutti<class '__main__.new'>
MarkHu

46

C'è un aiutante di raccolta chiamato namedtuple, che può fare questo per te:

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
Questo non risponde alla domanda di ricorsione per i dadi annidati.
derisibile il

4
non puoi modificare namedtuples.
Max

Possiamo garantire che l'ordine dell'elenco restituito da keys () e valori () corrisponda all'ordine? Voglio dire, se si tratta di un OrderedDict, sì. Ma un dict standard? So che vengono ordinati dicti "compatti" di CPython 3.6+ e PyPy, ma citando la documentazione: "L'aspetto che preserva l'ordine di questa nuova implementazione è considerato un dettaglio dell'implementazione e non dovrebbe essere invocato"
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

Può essere utilizzato con qualsiasi struttura sequenza / dict / value di qualsiasi profondità.


4
Questa dovrebbe essere la risposta. Funziona bene per la nidificazione. Puoi usarlo come object_hook anche per json.load ().
010110110101,

2
Simile alla risposta funzionale di SilentGhost del 2009: i dati del nodo foglia sono accessibili, ma i genitori / ramoscelli vengono visualizzati come riferimenti a oggetti. Per stampare piuttosto,def __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
MarkHu

5
Utenti di Python 3.x: è solo .items()invece che .iteritems()nella riga 4. (La funzione è stata rinominata, ma sostanzialmente identica )
neo post modern

Funziona perfettamente con oggetti nidificati, grazie! Come commento per i neofiti come me, per iteritems python3 () deve essere cambiato in items ()
Óscar Andreu

31

Prendendo ciò che ritengo siano gli aspetti migliori degli esempi precedenti, ecco cosa mi è venuto in mente:

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()))

Si noti che il costruttore può essere abbreviato in: il def __init__(self, dct): for k, v in dct.iteritems(): setattr(self, k, isinstance(v, dict) and self.__class__(v) or v) che rimuove anche la chiamata esplicita aStruct
George V. Reilly,

2
Preferirei non sottovalutare la mia risposta, ma ripensandoci ho notato che non ricorre nei tipi di sequenza. xd [1] .foo fallisce in questo caso.
Andyvanee,

2
il isinstance(v, dict)controllo sarebbe migliore in quanto isinstance(v, collections.Mapping)in grado di gestire le future cose simili a dict
piani cottura

30

Se il tuo dict proviene json.loads(), puoi invece trasformarlo in un oggetto (anziché in un dict) in una riga:

import json
from collections import namedtuple

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

Vedi anche Come convertire i dati JSON in un oggetto Python .


Sembra essere molto più lento del normale .loadsse hai un enorme oggetto JSON
michaelsnowden

16

Se si desidera accedere alle chiavi di dict come oggetto (o come dict per chiavi difficili), farlo in modo ricorsivo e anche essere in grado di aggiornare il dict originale, è possibile:

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)

Esempio di utilizzo:

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

Ho finito per provare ENTRAMBI l' attrice e il mazzolibrerie e le ho trovate troppo lente per i miei usi. Dopo che un amico e io lo abbiamo esaminato, abbiamo scoperto che il metodo principale per scrivere queste librerie risulta che la libreria ricorre in modo aggressivo attraverso un oggetto nidificato e fa copie dell'oggetto dizionario in tutto. Con questo in mente, abbiamo apportato due modifiche chiave. 1) Abbiamo creato attributi caricati in modo pigro 2) invece di creare copie di un oggetto dizionario, creiamo copie di un oggetto proxy leggero. Questa è l'implementazione finale. L'aumento delle prestazioni dell'uso di questo codice è incredibile. Quando si utilizza AttrDict o Bunch, queste due librerie da sole hanno consumato rispettivamente 1/2 e 1/3 del tempo della mia richiesta (cosa !?). Questo codice ha ridotto quel tempo a quasi nulla (da qualche parte nell'intervallo di 0,5 ms). Questo ovviamente dipende dalle tue esigenze,

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

Vedi l'implementazione originale qui da https://stackoverflow.com/users/704327/michael-merickel .

L'altra cosa da notare è che questa implementazione è piuttosto semplice e non implementa tutti i metodi di cui potresti aver bisogno. Dovrai scriverli come richiesto sugli oggetti DictProxy o ListProxy.


9

x.__dict__.update(d) dovrebbe andare bene.


grazie per la tua risposta, ma cos'è x? il dict o un oggetto standard? potresti darmi un suggerimento per favore.
Marc,

x è il tuo oggetto. Ogni oggetto ha un dict . Aggiornando il dict di un oggetto, in realtà stai aggiornando le variabili chiave in esso.
Alex Rodrigues,

Le parole in grassetto sono _ _ dict _ _
Alex Rodrigues

15
Questo non gestirà i dizionari nidificati.
FogleBird,

Non tutti gli oggetti hanno un __dict__: prova object().__dict__e otterraiAttributeError: 'object' object has no attribute '__dict__'
Will Manley,

8

È possibile sfruttare il jsonmodulo della libreria standard con un hook di oggetto personalizzato :

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)

Esempio di utilizzo:

>>> 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'

E non è strettamente di sola lettura come è namedtuple, ovvero è possibile modificare i valori, non la struttura:

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

1
La migliore risposta di gran lunga
Connor

Mi piace l'idea di utilizzare il meccanismo di caricamento json per elementi nidificati. Ma abbiamo già un dict e non mi piace il fatto che uno ha bisogno di crearne una stringa per mapparlo in un oggetto. Preferirei avere una soluzione che crea oggetti direttamente da un dict.
Sandro,

1
Deve prima convertirsi in stringa ma piuttosto generico in quanto si potrebbe estendere tramite json.JSONEncodere object_hook.
Sandro,

6

Questo dovrebbe iniziare:

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

Non funziona ancora per gli elenchi. Dovrai racchiudere gli elenchi in una UserList e sovraccaricare __getitem__per avvolgere i dadi.


2
Per farlo funzionare per gli elenchi, usa la if isinstance(d, list)clausola dalla Anonrisposta di.
Vinay Sajip,

6

So che ci sono già molte risposte qui e sono in ritardo alla festa, ma questo metodo ricorsivamente e 'sul posto' convertirà un dizionario in una struttura simile ad un oggetto ... Funziona 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

Cosa intendi per "struttura simile ad un oggetto"?
MoralCode

1
Simile agli oggetti a causa del principio di tipizzazione delle anatre. Voglio dire che puoi accedere alle sue proprietà come faresti se fosse un'istanza di una classe. ma NON è un oggetto in quel senso, è una tupla di nome.
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

Lascia che ti spieghi una soluzione che ho quasi usato qualche tempo fa. Ma prima, la ragione per cui non l'ho fatto è illustrata dal fatto che il seguente codice:

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

print x.from

dà questo errore:

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

Poiché "da" è una parola chiave Python, ci sono alcune chiavi del dizionario che non puoi consentire.


Ora la mia soluzione consente l'accesso agli elementi del dizionario utilizzando direttamente i loro nomi. Ma ti permette anche di usare la "semantica del dizionario". Ecco il codice con esempio di utilizzo:

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
class Struct: def init __ (self, ** iscrizioni): self .__ dict .update (iscrizioni)
Kenneth Reitz

4

Domande e risposte precedenti, ma ho ancora qualcosa da dire. Sembra che nessuno parli del dict ricorsivo. Questo è il mio codice:

#!/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

Volevo caricare la mia versione di questo piccolo paradigma.

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)

Conserva gli attributi per il tipo importato nella classe. La mia unica preoccupazione sarebbe sovrascrivere i metodi all'interno del dizionario durante l'analisi. Ma per il resto sembra solido!


Sebbene sia una buona idea, questo non sembra funzionare nell'esempio dell'OP, in effetti sembra modificare il dizionario passato! In effetti, df.anon funziona nemmeno.
Andy Hayden,

4

Anche questo funziona bene

class DObj(object):
    pass

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

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

3

Ecco un altro modo per implementare il suggerimento originale di SilentGhost:

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

Mi sono imbattuto nel caso che avevo bisogno di convertire ricorsivamente un elenco di dadi in un elenco di oggetti, quindi in base allo snippet di Roberto qui cosa ha funzionato per me:

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

Nota che qualsiasi tupla verrà convertita nel suo equivalente elenco, per ovvi motivi.

Spero che questo aiuti qualcuno tanto quanto le tue risposte hanno fatto per me, ragazzi.


3

Che ne dici di assegnare il tuo dictal __dict__di un oggetto vuoto?

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

Ovviamente questo non riesce nell'esempio di dict nidificato a meno che non lo cammini ricorsivamente:

# 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

E il tuo elemento elenco di esempio probabilmente doveva essere un Mapping, un elenco di coppie (chiave, valore) come questo:

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

2

Ecco un'altra implementazione:

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()))

[Modifica] Mancava un po 'anche la gestione dei dadi all'interno degli elenchi, non solo di altri dadi. Aggiunta correzione.


Si noti che l'impostazione di dict nel dizionario di origine significa che qualsiasi modifica agli attributi sull'oggetto risultante influirà anche sul dizionario che ha creato l'oggetto e viceversa. Ciò può portare a risultati imprevisti se il dizionario viene utilizzato per qualcosa di diverso dalla creazione dell'oggetto.
Mark Roddy,

@Mark: In realtà, un nuovo dizionario viene passato a DictObj ogni volta, invece di passare attraverso lo stesso oggetto dict, quindi non si verificherà effettivamente. È necessario farlo, poiché devo tradurre anche i valori all'interno di un dizionario, quindi sarebbe impossibile passare attraverso l'oggetto dict originale senza mutarlo da solo.
Brian,

Ci sono alcune risposte a questo, la risposta accettata non sembrava essere ricorsiva o gestire elenchi. Li ho letti tutti e questo è sembrato il più semplice a prima vista, l'ho provato e ha funzionato. Bella risposta.
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))

Uso:

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

Cosa ne pensi di questo:

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

Questo può quindi essere usato in questo modo:

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

2

Penso che un dict sia composto da numero, stringa e dict è abbastanza per la maggior parte del tempo. Quindi ignoro la situazione in cui tuple, elenchi e altri tipi non compaiono nella dimensione finale di un dict.

Considerando l'ereditarietà, combinata con la ricorsione, risolve il problema di stampa in modo conveniente e offre anche due modi per interrogare un dato, un modo per modificarlo.

Vedi l'esempio di seguito, un dict che descrive alcune informazioni sugli studenti:

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)

risultati:

{'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

In genere si desidera rispecchiare la gerarchia di dettatura nell'oggetto ma non elencare o tuple che sono in genere al livello più basso. Quindi è così che l'ho fatto:

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)

Quindi facciamo:

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

e prendi:

x.b.b1.x 1

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


1

Il mio dizionario è di questo formato:

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'}
    ]
}

Come si può vedere, ho dizionari nidificati e un elenco di dadi . Questo perché addr_bk è stato decodificato dai dati del buffer di protocollo convertiti in un dict python utilizzando lwpb.codec. Ci sono campi opzionali (ad es. Email => dove la chiave potrebbe non essere disponibile) e campi ripetuti (ad es. Telefono => convertiti in elenco di dict).

Ho provato tutte le soluzioni sopra proposte. Alcuni non gestiscono bene i dizionari nidificati. Altri non possono stampare facilmente i dettagli dell'oggetto.

Solo la soluzione, dict2obj (dict) di Dawie Strauss, funziona meglio.

L'ho migliorato un po 'da gestire quando non è possibile trovare la chiave:

# 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

Quindi, l'ho provato con:

# 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

Costruire la mia risposta a " python: come aggiungere proprietà a una classe in modo dinamico? ":

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

Chiami makedatail dizionario che vuoi convertire, o forse a trydataseconda di cosa ti aspetti come input, e sputa fuori un oggetto dati.

Appunti:

  • Puoi aggiungere elif a trydatase hai bisogno di più funzionalità.
  • Ovviamente questo non funzionerà se lo desideri x.a = {}o simili.
  • Se si desidera una versione di sola lettura, utilizzare i dati della classe dalla risposta originale .
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.