web-dev-qa-db-fra.com

Convertissez Python dict imbriqué en objet?

Je recherche un moyen élégant d’obtenir des données en utilisant l’accès par attribut à un dict avec des dictons et des listes imbriquées (syntaxe d’objet de style javascript).

Par exemple:

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

Devrait être accessible de cette façon:

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

Je pense que ce n’est pas possible sans récursion, mais quel serait un bon moyen d’obtenir un style d’objet pour dict?

498
Marc

Mise à jour: Dans Python 2.6 et versions ultérieures, déterminez si la structure de données namedtuple répond à vos besoins:

>>> 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'alternative (contenu de la réponse d'origine) est la suivante:

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

Ensuite, vous pouvez utiliser:

>>> args = {'a': 1, 'b': 2}
>>> s = Struct(**args)
>>> s
<__main__.Struct instance at 0x01D6A738>
>>> s.a
1
>>> s.b
2
628
Eli Bendersky
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'
101
Nadia Alramli

Étonnamment, personne n'a mentionné Bunch . Cette bibliothèque est exclusivement destinée à fournir un accès de type attribut aux objets dict et fait exactement ce que le PO veut. Une démonstration:

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

Une bibliothèque Python 3 est disponible à l'adresse https://github.com/Infinidat/munch - Le crédit va à codyz

89
kontinuity
x = type('new_dict', (object,), d)

puis ajoutez la récursivité à cela et vous avez terminé.

edit voici comment je le mettrais en oeuvre:

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

Il existe un assistant de collecte appelé namedtuple , qui peut le faire pour vous:

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
47
umbrae

En prenant ce que je ressens comme les meilleurs aspects des exemples précédents, voici ce que je propose:

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

Peut être utilisé avec n'importe quelle structure séquence/dict/valeur de n'importe quelle profondeur.

30
XEye

Si votre dict provient de json.loads(), vous pouvez le transformer en objet (plutôt qu'en dict) sur une seule ligne:

import json
from collections import namedtuple

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

Voir aussi Comment convertir des données JSON en un objet Python .

27
DS.

Si vous souhaitez accéder aux clés de dict en tant qu'objet (ou en tant que dict pour des clés difficiles), le faire de manière récursive et pouvoir également mettre à jour le dict d'origine, vous pouvez:

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)

Exemple d'utilisation:

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'}}
14
user1783597
>>> 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'
13
Anon

J'ai fini par essayer les bibliothèques AttrDict et Bunch et je les ai trouvées trop lentes pour mes utilisations. Après un ami et moi avons examiné, nous avons constaté que la méthode principale pour écrire ces bibliothèques entraîne la récursivité agressive de la bibliothèque à travers un objet imbriqué et la copie de l’objet dictionnaire. Dans cet esprit, nous avons apporté deux modifications essentielles. 1) Nous avons créé des attributs chargés paresseux 2) Au lieu de créer des copies d'un objet dictionnaire, nous créons des copies d'un objet proxy léger. Ceci est la mise en œuvre finale. L'augmentation des performances de l'utilisation de ce code est incroyable. Lors de l'utilisation de AttrDict ou Bunch, ces deux bibliothèques consommaient à elles seules respectivement 1/2 et 1/3 de mon temps de requête (quoi !?). Ce code a réduit ce temps à presque rien (quelque part dans la plage de 0,5 ms). Cela dépend bien sûr de vos besoins, mais si vous utilisez cette fonctionnalité dans votre code, optez pour quelque chose de simple comme celui-ci.

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

Voir l'implémentation d'origine ici par https://stackoverflow.com/users/704327/michael-merickel .

L'autre chose à noter est que cette implémentation est assez simple et n'implémente pas toutes les méthodes dont vous pourriez avoir besoin. Vous devrez les écrire au besoin sur les objets DictProxy ou ListProxy.

9
JayD3e

x.__dict__.update(d) devrait bien se passer.

7
Alex Rodrigues

Cela devrait vous aider à démarrer:

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

Cela ne fonctionne pas encore pour les listes. Vous devrez envelopper les listes dans une UserList et surcharger __getitem__ pour envelopper les dicts.

6
Aaron Digulla

Je sais qu'il y a déjà beaucoup de réponses ici et que je suis en retard pour la fête, mais cette méthode va récursivement convertir un dictionnaire en une structure semblable à un objet ... Fonctionne en 3.x.x

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
5

Laissez-moi vous expliquer une solution que j'ai presque utilisée il y a quelque temps. Mais premièrement, la raison pour laquelle je ne l’ai pas fait est illustrée par le fait que le code suivant:

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

print x.from

donne cette erreur:

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

Parce que "de" est un mot clé Python, vous ne pouvez pas autoriser certaines clés de dictionnaire.


Maintenant, ma solution permet d'accéder aux éléments du dictionnaire en utilisant directement leurs noms. Mais cela vous permet également d'utiliser la "sémantique du dictionnaire". Voici le code avec un exemple d'utilisation:

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"
4
Dawie Strauss

Vous pouvez utiliser le json module de la bibliothèque standard avec un hook d'objet personnalisé:

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)

Exemple d'utilisation:

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

Et c’est pas strictement en lecture seule comme avec namedtuple, c’est-à-dire que vous pouvez changer les valeurs - pas la structure:

>>> o.b.c = 3
>>> o.b.c
3
4
Vanni Totaro

Vieux Q & A, mais je reçois quelque chose de plus à parler. Il semble que personne ne parle de dict récursif. Ceci est mon 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 __== '__main__':
    test01()
    test02()
4
truease.com

Cela fonctionne aussi bien aussi

class DObj(object):
    pass

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

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

Je voulais télécharger ma version de ce petit paradigme.

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)

Il préserve les attributs du type importé dans la classe. Mon seul souci serait d'écraser les méthodes du dictionnaire de votre analyse. Mais sinon ça semble solide!

4
Erik

Qu'en est-il d'assigner simplement votre dict au __dict__ d'un objet vide?

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

Bien sûr, cela ne fonctionne pas dans votre exemple de dict imbriqué, sauf si vous parcourez le dict de manière récursive:

# 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

Et votre exemple d'élément de liste était probablement censé être un Mapping, une liste de paires (clé, valeur) comme celle-ci:

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

Je suis tombé par hasard sur le cas où j'avais besoin de convertir récursivement une liste de dict en liste d'objets. Je me suis donc basé sur l'extrait de code de Roberto pour décrire le travail que j'ai effectué pour moi:

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

Notez que tout tuple sera converti en liste équivalente pour des raisons évidentes.

J'espère que cela aide quelqu'un autant que toutes vos réponses l'ont été pour moi, les gars.

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

# We got
# my_data.a == 1
3
forward
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))

Usage:

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
Paulo Freitas

Que dis-tu de ça:

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

Ceci peut alors être utilisé comme ceci:

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

Voici une autre implémentation:

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

[Edit] Un peu oublié sur la gestion des dict au sein des listes, pas seulement d'autres dict. Correction ajoutée.

2
Brian

Voici un autre moyen de mettre en œuvre la suggestion originale de 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
2
rob

Je pense qu'un dict est composé d'un nombre, d'une chaîne et que le dict est suffisant la plupart du temps. J'ignore donc la situation qui fait que les n-uplets, les listes et les autres types n'apparaissent pas dans la dernière dimension d'un dict.

Considérant l'héritage, combiné à la récursivité, il résout le problème d'impression de manière pratique et fournit également deux méthodes pour interroger des données, une méthode pour modifier des données.

Voir l'exemple ci-dessous, un dict qui décrit des informations sur les étudiants:

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)

Résultats:

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

Construire ma réponse à " python: Comment ajouter une propriété à une classe dynamiquement? ":

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

Vous appelez makedata dans le dictionnaire à convertir, ou peut-être trydata, en fonction de ce que vous attendez en tant qu'entrée, et il crache un objet de données.

Remarques:

  • Vous pouvez ajouter des elifs à trydata si vous avez besoin de plus de fonctionnalités.
  • Évidemment, cela ne fonctionnera pas si vous voulez x.a = {} ou similaire.
  • Si vous voulez une version en lecture seule, utilisez les données de classe de la réponse d'origine .
1
David X

Voici une version prête à être imbriquée avec namedtuple:

from collections import namedtuple

class Struct(object):
    def __new__(cls, data):
        if isinstance(data, dict):
            return namedtuple(
                'Struct', data.iterkeys()
            )(
                *(Struct(val) for val in data.values())
            )
        Elif isinstance(data, (Tuple, list, set, frozenset)):
            return type(data)(Struct(_) for _ in data)
        else:
            return data

=>

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> s = Struct(d)
>>> s.d
['hi', Struct(foo='bar')]
>>> s.d[0]
'hi'
>>> s.d[1].foo
'bar'
1
lajarre

Cette petite classe ne me pose jamais aucun problème, développez-la et utilisez la méthode copy ():

  import simplejson as json

  class BlindCopy(object):

    def copy(self, json_str):
        dic = json.loads(json_str)
        for k, v in dic.iteritems():
            if hasattr(self, k):
                setattr(self, k, v);
0
thiago marini

Mon dictionnaire est de ce 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]'}
    ]
}

Comme on peut le constater, j'ai dictionnaires imbriqués et liste de dictés. En effet, addr_bk a été décodé à partir des données du tampon de protocole converties en un fichier python à l'aide de lwpb.codec. Il existe des champs facultatifs (par exemple, email => où la clé peut être indisponible) et des champs répétés (par exemple, téléphone => converti en liste de dict).

J'ai essayé toutes les solutions proposées ci-dessus. Certains ne gèrent pas bien les dictionnaires imbriqués. D'autres ne peuvent pas imprimer facilement les détails de l'objet.

Seule la solution, dict2obj (dict) de Dawie Strauss, fonctionne le mieux.

Je l'ai amélioré un peu pour gérer lorsque la clé ne peut pas être trouvée:

# 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

Ensuite, je l'ai testé avec:

# 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
0
Whospal

J'ai eu quelques problèmes avec __getattr__ n'étant pas appelé alors j'ai construit une nouvelle version de classe de style:

class Struct(object):
    '''The recursive class for building and representing objects with.'''
    class NoneStruct(object):
        def __getattribute__(*args):
            return Struct.NoneStruct()

        def __eq__(self, obj):
            return obj == None

    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 __getattribute__(*args):
        try:
            return object.__getattribute__(*args)
        except:            
            return Struct.NoneStruct()

    def __repr__(self):
        return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for 
(k, v) in self.__dict__.iteritems()))

Cette version a également l'ajout de NoneStruct qui est renvoyé lorsqu'un attribut appelé n'est pas défini. Cela permet à aucun test de voir si un attribut est présent. Très utile lorsque l'entrée dict exacte n'est pas connue (réglages, etc.).

bla = Struct({'a':{'b':1}})
print(bla.a.b)
>> 1
print(bla.a.c == None)
>> True
0
RickyA

Convertir dict en object

from types import SimpleNamespace

def dict2obj(data):
    """将字典对象转换为可访问的对象属性"""
    if not isinstance(data, dict):
        raise ValueError('data must be dict object.')

    def _d2o(d):
        _d = {}
        for key, item in d.items():
            if isinstance(item, dict):
                _d[key] = _d2o(item)
            else:
                _d[key] = item
        return SimpleNamespace(**_d)

    return _d2o(data)

Réponse de référence

0
David_li

Voici un autre moyen alternatif de convertir une liste de dictionnaires en un objet:

def dict2object(in_dict):
    class Struct(object):
        def __init__(self, in_dict):
            for key, value in in_dict.items():
                if isinstance(value, (list, Tuple)):
                    setattr(
                        self, key,
                        [Struct(sub_dict) if isinstance(sub_dict, dict)
                         else sub_dict for sub_dict in value])
                else:
                    setattr(
                        self, key,
                        Struct(value) if isinstance(value, dict)
                        else value)
    return [Struct(sub_dict) for sub_dict in in_dict] \
        if isinstance(in_dict, list) else Struct(in_dict)
0
İbrahim Gündüz