Convertir un dict Python imbriqué en objet?

540

Je cherche un moyen élégant d'obtenir des données en utilisant l'accès aux attributs sur un dict avec quelques dict et listes imbriqués (c'est-à-dire une syntaxe d'objet de style javascript).

Par exemple:

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

Devrait être accessible de cette manière:

>>> 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écursivité, mais quelle serait une bonne façon d'obtenir un style d'objet pour les dict?

Marc
la source
6
J'essayais de faire quelque chose de similaire récemment, mais une clé de dictionnaire récurrente ("de" - qui est un mot-clé Python) m'a empêché de continuer. Parce que dès que vous essayez d'utiliser "x.from" pour accéder à cet attribut, vous obtenez une erreur de syntaxe.
Dawie Strauss
3
c'est un problème en effet, mais je peux abandonner sur "de" pour faciliter la vie en accédant aux grandes constructions de dict :) taper x ['a'] ['d'] [1] ['foo'] est vraiment ennuyeux, donc xad [1] règles .foo. si vous avez besoin de, vous pouvez y accéder via getattr (x, 'from') ou utiliser _from comme attribut à la place.
Marc
5
from_plutôt que _fromselon PEP 8 .
Kos
1
Vous pouvez utiliser getattr(x, 'from')au lieu de renommer l'attribut.
George V. Reilly
3
La plupart de ces "solutions" ne semblent pas fonctionner (même la solution acceptée, ne permet pas l'imbrication d1.b.c), je pense qu'il est clair que vous devriez utiliser quelque chose d'une bibliothèque, par exemple nommétuple de collections , comme cette réponse le suggère, .. .
Andy Hayden

Réponses:

661

Mise à jour: dans Python 2.6 et versions ultérieures, déterminez si la namedtuplestructure des données convient à 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:

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
Eli Bendersky
la source
19
Même chose ici - c'est particulièrement utile pour reconstruire des objets Python à partir de bases de données orientées document comme MongoDB.
mikemaccana
13
Pour obtenir une impression plus jolie, ajoutez: def repr __ (self): return '<% s>'% str ('\ n' .join ('% s:% s'% (k, repr (v)) for (k, v ) in self .__ dict .iteritems ()))
six8
16
Est-ce que cela fonctionnera avec des dictionnaires imbriqués? et dits contenant des objets et / ou une liste etc. Y a-t-il des captures?
Sam Stoelinga
5
@Sam S: il ne créera pas de Structs imbriqués à partir de dictionnaires imbriqués, mais en général le type de la valeur peut être n'importe quoi. La clé est limitée à un emplacement d'objet
Eli Bendersky
52
-1 parce que a) il ne fonctionne pas pour les dicts imbriquées, qui a clairement demandé à la question, et b) les mêmes fonctionnalités existent actuellement dans les bibliothèques standard dans argparse.Namespace(qui a également défini __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
la source
7
Bien! Je remplacerais .items () par .iteritems (), cependant, pour une empreinte mémoire plus petite.
Eric O Lebigot
7
Si ce n'est pas une exigence OP, ce n'est pas un problème - mais notez que cela ne traitera pas récursivement les objets dans les listes dans les listes.
Anon
3
Je me rends compte que c'est une vieille réponse, mais ces jours-ci, il serait préférable d'utiliser une classe de base abstraite au lieu de cette ligne laideif isinstance(b, (list, tuple)):
wim
4
Conseil: Demandez à la objclasse d'hériter de argparse.Namespacefonctionnalités supplémentaires telles que la représentation de chaînes lisibles.
Serrano
2
Selon le cas d'utilisation, nous vérifions généralement qu'il ne s'agit pas d' un type de chaîne mais qu'il s'agit d' un type de séquence
wim
108

Étonnamment, personne n'a mentionné Bunch . Cette bibliothèque est exclusivement destinée à fournir un accès au style d'attribut aux objets dict et fait exactement ce que l'OP 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 sur https://github.com/Infinidat/munch - Le crédit revient à codyzu

continuité
la source
16
Et il y a une branche compatible Bython (semble être 2.6-3.4) de Bunch nommée Munch: github.com/Infinidat/munch
codyzu
Bunch est la meilleure solution de tous ces éléments car il prend bien en charge plusieurs types de sérialisation et est maintenu. Super merci. Je souhaite que la version python3 porte le même nom, car pourquoi pas.
Jonathan
4
Donc juste une mise en garde, Bunch et Attrdict aussi d'ailleurs sont très lents. Ils ont consommé environ 1/3 et 1/2 de mon temps de demande respectivement lorsqu'ils ont vu une utilisation fréquente dans notre application. Certainement pas quelque chose à ignorer. Parlez-en plus stackoverflow.com/a/31569634/364604 .
JayD3e
Bien que cela permette un accès de type objet aux dictés, il le fait via getattr . Cela rend difficile l'introspection dans les REPL intelligentes comme IPython.
GDorn
Curieux de savoir comment cela se compare à JSONpath github.com/kennknowles/python-jsonpath-rw
MarkHu
64
x = type('new_dict', (object,), d)

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

éditez comme je l'implémenterais:

>>> 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
la source
1
Pourquoi créez-vous des objets-types sans les instancier? Ne serait-ce pas plus logique? Je veux dire, pourquoi ne pas le faire top_instance = top()et le retourner là où vous revenez top?
pancake
6
Bien pour les données "feuilles", mais les exemples xx.b<class '__main__.new'>
omettent
46

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
umbrae
la source
28
Cela ne répond pas à la question de la récursivité pour les dict imbriqués.
dérigible
4
vous ne pouvez pas modifier les couples nommés.
Max
Pouvons-nous garantir que l'ordre de la liste retournée par les touches () et les valeurs () correspond dans l'ordre? Je veux dire, si c'est un OrderedDict, oui. Mais un dict standard? Je sais que les dicts "compacts" CPython 3.6+ et PyPy sont commandés, mais citant la documentation: "L'aspect préservant l'ordre de cette nouvelle implémentation est considéré comme un détail d'implémentation et ne doit pas être invoqué"
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

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

XEye
la source
4
Cela devrait être la réponse. Cela fonctionne bien pour la nidification. Vous pouvez également l'utiliser comme un objet_hook pour json.load ().
010110110101
2
Semblable à la réponse fonctionnelle de SilentGhost de 2009 - les données du nœud feuille sont accessibles, mais les parents / brindilles s'affichent comme références d'objet. Pour joliment imprimer,def __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
MarkHu
5
Utilisateurs de Python 3.x: c'est juste à la .items()place de la .iteritems()ligne 4. (La fonction a été renommée, mais fait essentiellement la même chose )
neo post modern
Fonctionne parfaitement pour les objets imbriqués, merci! En tant que débutants comme moi, pour iteritems python3 () doit être changé en items ()
Óscar Andreu
31

En prenant ce que je pense être les meilleurs aspects des exemples précédents, voici ce que j'ai trouvé:

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
la source
Notez que le constructeur peut être raccourci en: def __init__(self, dct): for k, v in dct.iteritems(): setattr(self, k, isinstance(v, dict) and self.__class__(v) or v) ce qui supprime également l'appel explicite àStruct
George V. Reilly
2
Je préfère ne pas dévaloriser ma propre réponse, mais en y repensant, j'ai remarqué qu'elle ne récurrente pas dans les types de séquence. xd [1] .foo échoue dans ce cas.
andyvanee
2
le isinstance(v, dict)chèque serait mieux car isinstance(v, collections.Mapping)il peut gérer les futures choses de type dict
plaques de cuisson
30

Si votre dict provient json.loads(), vous pouvez le transformer en objet à la place (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 .

DS.
la source
Semble être beaucoup plus lent que d'habitude .loadssi vous avez un énorme objet JSON
michaelsnowden
16

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

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'}}
user1783597
la source
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
la source
12

J'ai fini par essayer les deux AttrDict et Bunchbibliothèques et les a trouvés beaucoup trop lents pour mes utilisations. Après qu'un ami et moi avons examiné la question, nous avons constaté que la principale méthode d'écriture de ces bibliothèques entraînait la récurrence agressive de la bibliothèque à travers un objet imbriqué et la création de copies de l'objet dictionnaire. Dans cet esprit, nous avons apporté deux changements clés. 1) Nous avons créé des attributs chargés paresseusement 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. Lorsque j'utilise AttrDict ou Bunch, ces deux bibliothèques à elles seules ont consommé respectivement 1/2 et 1/3 de mon temps de demande (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,

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 originale ici par https://stackoverflow.com/users/704327/michael-merickel .

L'autre chose à noter, c'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 comme requis sur les objets DictProxy ou ListProxy.

JayD3e
la source
9

x.__dict__.update(d) devrait bien faire.

Alex Rodrigues
la source
merci pour votre réponse, mais qu'est-ce que x? le dict ou un objet standard? pourriez-vous me donner un indice s'il vous plaît.
Marc
x est votre objet. Chaque objet a un dict . En mettant à jour le dict d'un objet, vous mettez à jour les variables clés qu'il contient.
Alex Rodrigues
Les mots en gras sont _ _ dict _ _
Alex Rodrigues
15
Cela ne gérera pas les dictionnaires imbriqués.
FogleBird
Tous les objets n'ont pas un __dict__: essayez object().__dict__et vous obtiendrezAttributeError: 'object' object has no attribute '__dict__'
Will Manley
8

Vous pouvez tirer parti du jsonmodule de la bibliothèque standard avec un crochet 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 ce n'est pas strictement en lecture seule comme c'est le cas avec namedtuple, c'est-à-dire que vous pouvez changer les valeurs - pas la structure:

>>> o.b.c = 3
>>> o.b.c
3
Vanni Totaro
la source
1
Meilleure réponse de loin
Connor
J'aime l'idée d'utiliser le mécanisme de chargement json pour les éléments imbriqués. Mais nous avons déjà un dict et je n'aime pas le fait que l'on ait besoin d'en créer une chaîne pour la mapper en un objet. Je préfère avoir une solution qui crée des objets directement à partir d'un dict.
Sandro
1
Doit d'abord être converti en chaîne mais assez générique car on pourrait l'étendre via json.JSONEncoderet object_hook.
Sandro
6

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 encapsuler les listes dans une UserList et surcharger __getitem__pour encapsuler les dict.

Aaron Digulla
la source
2
Pour le faire fonctionner pour les listes, utilisez la if isinstance(d, list)clause de Anonla réponse de.
Vinay Sajip
6

Je sais qu'il y a déjà beaucoup de réponses ici et je suis en retard à la fête, mais cette méthode convertira récursivement et «en place» un dictionnaire en une structure de type objet ... Fonctionne en 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
la source
Que voulez-vous dire par "structure semblable à un objet"?
MoralCode
1
Comme un objet en raison du principe de typage du canard. Ce que je veux dire, c'est que vous pouvez accéder à ses propriétés comme vous le feriez s'il s'agissait d'une instance d'une classe. mais ce n'est PAS un objet dans ce sens, c'est un tuple nommé.
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
vers l'avant
la source
4

Permettez-moi d'expliquer une solution que j'ai presque utilisée il y a quelque temps. Mais d'abord, la raison pour laquelle je ne l'ai pas 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, il y a certaines clés de dictionnaire que vous ne pouvez pas autoriser.


Maintenant, ma solution permet d'accéder aux éléments du dictionnaire en utilisant directement leurs noms. Mais il vous permet également d'utiliser la "sémantique des dictionnaires". 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"
Dawie Strauss
la source
1
structure Struct: def init __ (auto, ** entrées): self .__ dict .update (entrées)
Kenneth Reitz
4

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

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 conserve les attributs du type importé dans la classe. Ma seule préoccupation serait d'écraser les méthodes du dictionnaire dans votre analyse. Mais sinon, ça semble solide!

Erik
la source
Bien que ce soit une bonne idée, cela ne semble pas fonctionner pour l'exemple de l'OP, en fait, cela semble modifier le passé dans le dictionnaire! En fait, df.aça ne marche même pas.
Andy Hayden
4

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
naren
la source
3

Voici une autre façon 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
Rob
la source
3

Je suis tombé sur le cas dont j'avais besoin pour convertir récursivement une liste de dict en liste d'objets, donc sur la base de l'extrait de Roberto ici ce qui a fait le travail 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 son équivalent de liste, pour des raisons évidentes.

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

NiKo
la source
3

Qu'en est-il de simplement attribuer votre dictà l' __dict__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 échoue sur votre exemple de dict imbriqué, sauf si vous le faites récursivement:

# 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 une Mapping, une liste de paires (clé, valeur) comme ceci:

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

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

[Modifier] Bit manquant sur la gestion des dictés dans les listes, pas seulement d'autres dictés. Ajout d'un correctif.

Brian
la source
Notez que la définition de dict dans le dictionnaire source signifie que toute modification des attributs sur l'objet résultant affectera également le dictionnaire qui a créé l'objet, et vice-versa. Cela peut conduire à des résultats inattendus si le dictionnaire est utilisé pour autre chose que la création de l'objet.
Mark Roddy
@Mark: En fait, un nouveau dictionnaire est transmis à DictObj à chaque fois, plutôt que de simplement passer par le même objet dict, donc cela ne se produira pas réellement. Il est nécessaire de le faire, car j'ai également besoin de traduire les valeurs dans un dictionnaire, il serait donc impossible de passer par l'objet dict d'origine sans le muter moi-même.
Brian
Il y a pas mal de réponses à cela, la réponse acceptée ne semblait pas être récursive ou gérer des listes. Je les ai tous lus et celui-ci avait l'air le plus simple à première vue, j'ai testé et cela a fonctionné. Très bonne réponse.
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))

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
Paulo Freitas
la source
2

Que dis-tu de ça:

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

Cela peut ensuite être utilisé comme ceci:

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

Je pense qu'un dict se compose de nombre, de chaîne et de dict est suffisant la plupart du temps. J'ignore donc la situation où les tuples, les listes et les autres types n'apparaissent pas dans la dimension finale d'un dict.

Compte tenu de l'héritage, combiné à la récursivité, il résout le problème d'impression de manière pratique et fournit également deux façons d'interroger des données, une façon de modifier les données.

Voir l'exemple ci-dessous, un dict qui décrit certaines 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': ''}
tcpiper
la source
2

En règle générale, vous souhaitez mettre en miroir la hiérarchie dict dans votre objet, mais pas la liste ou les tuples qui sont généralement au niveau le plus bas. Voilà donc comment je l'ai fait:

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)

Nous faisons donc:

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

et obtenir:

x.b.b1.x 1

x.c [«salut», «bar»]

VengaVenga
la source
1

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 voir, j'ai des dictionnaires imbriqués et une liste de dict . En effet, l'addr_bk a été décodé à partir des données du tampon de protocole converties en dict python à l'aide de lwpb.codec. Il y a un champ optionnel (par exemple email => où la clé peut ne pas être disponible) et un champ répété (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 un peu amélioré pour gérer quand la clé est introuvable:

# 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
Whospal
la source
1

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

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 makedatale dictionnaire que vous souhaitez convertir, ou peut-être en trydatafonction de ce que vous attendez en 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 le souhaitez x.a = {} ou similaire.
  • Si vous souhaitez une version en lecture seule, utilisez les données de classe de la réponse d'origine .
David X
la source