How to convert a nested Python dict to object?
Asked Answered
B

45

673

I'm searching for an elegant way to get data using attribute access on a dict with some nested dicts and lists (i.e. javascript-style object syntax).

For example:

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

Should be accessible in this way:

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

I think, this is not possible without recursion, but what would be a nice way to get an object style for dicts?

Bondy answered 20/8, 2009 at 11:28 Comment(6)
I was trying to do something similar recently, but a recurring dictionary key ("from" - which is a Python keyword) prevented me from going through with it. Because as soon as you tried using "x.from" to access that attribute you'd get a syntax error.Scut
that's a problem indeed, but i can abandon on "from" to make life easier in accessing large dict constructs :) typing x['a']['d'][1]['foo'] is really annoying, so x.a.d[1].foo rules. if you need from, you can access it via getattr(x, 'from') or use _from as attribute instead.Bondy
from_ rather than _from according to PEP 8.Coriander
You can use getattr(x, 'from') instead of renaming the attribute.Conciseness
Most of these "solutions" don't seem to work (even the accepted one, doesn't allow nested d1.b.c), I think it's clear you should be using something from a library, e.g. namedtuple from collections, as this answer suggests, ...Stillbirth
Bunch - Use a Python dict like an object: thechangelog.com/bunch-lets-use-python-dict-like-objectBondy
T
750

Update: In Python 2.6 and onwards, consider whether the namedtuple data structure suits your needs:

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

The alternative (original answer contents) is:

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

Then, you can use:

>>> args = {'a': 1, 'b': 2}
>>> s = Struct(**args)
>>> s
<__main__.Struct instance at 0x01D6A738>
>>> s.a
1
>>> s.b
2
Tuning answered 20/8, 2009 at 11:55 Comment(12)
Same here - this is particularly useful for reconstructing Python objects from document oriented databases like MongoDB.Katzenjammer
To get prettier printing add: def repr__(self): return '<%s>' % str('\n '.join('%s : %s' % (k, repr(v)) for (k, v) in self.__dict.iteritems()))Araucaria
Will this work with nested dictionaries? and dicts containing objects and or list etc. Are there any catches?Photoelasticity
@Sam S: it won't create nested Structs from nested dictionaries, but in general the type of the value can be anything. Key is restricted to being suitable for an object slotTuning
Thanks! I was dismayed when passing user-defined objects through xmlrpclib only to find them converted into a dict representation. This useful code rectifies that issue.Ewell
This helps access useful dicts in deeply nested JSON.Jurdi
-1 because a) it doesn't work for nested dicts, which the question clearly asked for, and b) the same functionality currently exists in standard libraries in argparse.Namespace (which has also defined __eq__, __ne__, __contains__).Shopping
Do you have a solution for nested dictionaries, too?Stridulate
Future readers - refer to @umbrae's answer with the namedtupleVoguish
For nested dictionaries, look at one of the other Struct() answers below with the recursive _wrap() function.Phytogeography
@Araucaria Fixed formatting (mostly): def __repr__(self): return '<%s>' % str('\n '.join('%s : %s' % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))Salangi
So... this requires you to manually reformat the dictionary into keyword arguments? What if you have no idea what the dictionary will look like?Vondavonni
L
182

Surprisingly no one has mentioned Bunch. This library is exclusively meant to provide attribute style access to dict objects and does exactly what the OP wants. A demonstration:

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

A Python 3 library is available at https://github.com/Infinidat/munch - Credit goes to codyzu

>>> from munch import DefaultMunch
>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> obj = DefaultMunch.fromDict(d)
>>> obj.b.c
2
>>> obj.a
1
>>> obj.d[1].foo
'bar'
Lodovico answered 20/7, 2014 at 16:30 Comment(7)
And there is a python 3 compatible (appears to be 2.6-3.4) branch of Bunch named Munch: github.com/Infinidat/munchDarvon
Bunch is the best solution of all of these because it supports multiple types of serialization well, and is maintained. Great, thank you. I wish the python3 version was named the same thing, buecause why not.Holster
So just a forewarning, Bunch and Attrdict as well for that matter are very slow. They consumed about 1/3 and 1/2 of my request time respectively when they saw frequent use in our application. Definitely not something to ignore. Talk more about it https://mcmap.net/q/20740/-how-to-convert-a-nested-python-dict-to-object.Consistency
While this does allow object-like access to dicts, it does so via getattr. This makes introspection in smart REPLs like IPython difficult.Tiffany
Curious how this compares to JSONpath github.com/kennknowles/python-jsonpath-rwDracaena
if use python 2.+ => pypi.org/project/bunch if use python 3.+ => pypi.org/project/munch As you are in python3 , use dict.items() instead of dict.iteritems() iteritems() was removed in python3, so you can't use this method anymore.Burck
AttributeError: 'dict' object has no attribute 'iteritems'Vat
A
140
class obj(object):
    def __init__(self, d):
        for k, v in d.items():
            if isinstance(k, (list, tuple)):
                setattr(self, k, [obj(x) if isinstance(x, dict) else x for x in v])
            else:
                setattr(self, k, obj(v) if isinstance(v, dict) else v)

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> x = obj(d)
>>> x.b.c
2
>>> x.d[1].foo
'bar'
Adipocere answered 20/8, 2009 at 11:58 Comment(11)
Good! I'd replace .items() by .iteritems(), though, for a smaller memory footprint.Presbyterial
If not an OP requirement, this is not an issue - but note that this won't recursively process objects in lists within lists.Waltz
Nice! But then the dict needs to be complete when you create the class... I guess that works... you could create another instance if you modify the dict. Good stuff!Uniparous
Thank you this worked well for me. Here is the reverse jic anyone can use it. def todict(self): d = {} for attr in dir(self): if attr.startswith('__') or attr == 'todict': continue val = getattr(self, attr) if isinstance(val, (list, tuple)): d[attr] = [x.todict() if isinstance(x, Bag) else x for x in val] else: d[attr] = val.todict() if isinstance(val, Bag) else val return dInduction
I realise this is an old answer, but these days it would be better to use an abstract base class instead of that ugly line if isinstance(b, (list, tuple)):Shopping
>>> x = {}; x['a'] = x; obj(x) .... RuntimeError: maximum recursion depth exceeded. Make sure there are no circular references in your (nested) dict structure.Proscenium
Tip: Have the obj class inherit from argparse.Namespace for additional features like readable string representation.Casuistry
@wim: What abstract base class from the collections module would you use to replace isinstance(b, (list, tuple))?Geriatrics
Depending on the use case, usually we would check that it is not a string type but that it is a Sequence typeShopping
I am wondering how an answer with a bug got 137 positive votes... if isinstance(k, (list, tuple)):: k is the key, you should test v instead!Passementerie
lol I have the same question as ishahak. Came here to say this.Voletta
C
77
x = type('new_dict', (object,), d)

then add recursion to this and you're done.

edit this is how I'd implement it:

>>> 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'
Chappell answered 20/8, 2009 at 11:31 Comment(2)
Why are you creating type-objects and not instantiating them? Wouldn't that be more logical? I mean, why not do top_instance = top() and returning that where you return top?Bluefarb
Nice for the "leaf" data, but the examples conveniently leave out "twigs" like x and x.b which return ugly <class '__main__.new'>Dracaena
S
68
# Applies to Python-3 Standard Library
class Struct(object):
    def __init__(self, data):
        for name, value in data.items():
            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


# Applies to Python-2 Standard Library
class Struct(object):
    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

Can be used with any sequence/dict/value structure of any depth.

Solis answered 9/8, 2011 at 8:58 Comment(5)
This should be the answer. It works well for nesting. You can use this as an object_hook for json.load() as well.Phytogeography
Similar to SilentGhost's functional answer from 2009 --the leaf node data is accessible, but the parent/twigs display as object references. To pretty-print, def __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))Dracaena
Python 3.x users: it's just .items() instead of .iteritems() in line 4. (The function was renamed, but does essentialy the same)Quotidian
Works perfect for nested objects, thanks! As a comment newbies as I, for python3 iteritems() must be changed to items()Galle
The pretty-print equivalent for python 3 is: def __repr__(self): return ("{ " + str(", ".join([f"'{k}': {v}" for k, v in [(k, repr(v)) for (k, v) in self.__dict__.items()]])) + " }")Mauricemauricio
R
57

There's a collection helper called namedtuple, that can do this for you:

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
Riggle answered 23/2, 2012 at 12:43 Comment(4)
This does not answer the question of recursion for the nested dicts.Arrangement
you can't modify namedtuples.Acidulant
Can we guarantee that the order of the list returned by keys() and values() match in order? I mean, if it is a OrderedDict, yes. But a standard dict? I know that CPython 3.6+ and PyPy "compact" dicts are ordered, but quoting documentation: "The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon"Backache
this also works d_named = namedtuple('Struct', d)(**d)Brandeebranden
V
39

If your dict is coming from json.loads(), you can turn it into an object instead (rather than a dict) in one line:

import json
from collections import namedtuple

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

See also How to convert JSON data into a Python object.

Votive answered 8/4, 2013 at 14:53 Comment(2)
Seems to be much slower than regular .loads if you have a huge JSON objectFogel
this is the answer. thanks mate.Ideograph
A
36

You can leverage the json module of the standard library with a custom object hook:

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)

Example usage:

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

And it is not strictly read-only as it is with namedtuple, i.e. you can change values – not structure:

>>> o.b.c = 3
>>> o.b.c
3
Albie answered 25/1, 2016 at 16:11 Comment(3)
Best answer by farCincture
I like the idea to use the json loading mechanism for nested elements. But we already have a dict and I don't like the fact the one needs to create a string out of it to map it into an object. I would rather like to have a solution that creates objects directly from a dict.Termitarium
Needs to convert to string first but pretty generic as one could extend it via json.JSONEncoder and object_hook.Termitarium
L
31

Taking what I feel are the best aspects of the previous examples, here's what I came up with:

class Struct:
    """The recursive class for building and representing objects with."""

    def __init__(self, obj):
        for k, v in obj.items():
            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__.items()))
Less answered 4/7, 2011 at 16:12 Comment(3)
Note that the constructor can be shortened to: def __init__(self, dct): for k, v in dct.iteritems(): setattr(self, k, isinstance(v, dict) and self.__class__(v) or v) which also removes the explicit call to StructConciseness
I'd rather not downvote my own answer, but looking back on this I've noticed that it doesn't recurse into sequence types. x.d[1].foo fails in this case.Less
the isinstance(v, dict) check would be better as isinstance(v, collections.Mapping) so it can handle future dict-like thingsVision
C
23

I ended up trying BOTH the AttrDict and the Bunch libraries and found them to be way too slow for my uses. After a friend and I looked into it, we found that the main method for writing these libraries results in the library aggressively recursing through a nested object and making copies of the dictionary object throughout. With this in mind, we made two key changes. 1) We made attributes lazy-loaded 2) instead of creating copies of a dictionary object, we create copies of a light-weight proxy object. This is the final implementation. The performance increase of using this code is incredible. When using AttrDict or Bunch, these two libraries alone consumed 1/2 and 1/3 respectively of my request time(what!?). This code reduced that time to almost nothing(somewhere in the range of 0.5ms). This of course depends on your needs, but if you are using this functionality quite a bit in your code, definitely go with something simple like this.

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

See the original implementation here by https://stackoverflow.com/users/704327/michael-merickel.

The other thing to note, is that this implementation is pretty simple and doesn't implement all of the methods you might need. You'll need to write those as required on the DictProxy or ListProxy objects.

Consistency answered 22/7, 2015 at 17:6 Comment(1)
Note that for Python 3.10 and above, you'll need to use Mapping and Sequence from collections.abc instead of collectionsCodicil
B
20

If you want to access dict keys as an object (or as a dict for difficult keys), do it recursively, and also be able to update the original dict, you could do:

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)

Example usage:

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'}}
Bosson answered 23/3, 2013 at 16:41 Comment(0)
W
16
>>> 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'
Waltz answered 20/8, 2009 at 11:50 Comment(0)
F
13

In 2021, use pydantic BaseModel - will convert nested dicts and nested json objects to python objects and vice versa:

https://pydantic-docs.helpmanual.io/usage/models/

>>> class Foo(BaseModel):
...     count: int
...     size: float = None
... 
>>> 
>>> class Bar(BaseModel):
...     apple = 'x'
...     banana = 'y'
... 
>>> 
>>> class Spam(BaseModel):
...     foo: Foo
...     bars: List[Bar]
... 
>>> 
>>> m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}])

Object to dict

>>> print(m.dict())
{'foo': {'count': 4, 'size': None}, 'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y'}]}

Object to JSON

>>> print(m.json())
{"foo": {"count": 4, "size": null}, "bars": [{"apple": "x1", "banana": "y"}, {"apple": "x2", "banana": "y"}]}

Dict to object

>>> spam = Spam.parse_obj({'foo': {'count': 4, 'size': None}, 'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y2'}]})
>>> spam
Spam(foo=Foo(count=4, size=None), bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y2')])

JSON to object

>>> spam = Spam.parse_raw('{"foo": {"count": 4, "size": null}, "bars": [{"apple": "x1", "banana": "y"}, {"apple": "x2", "banana": "y"}]}')
>>> spam
Spam(foo=Foo(count=4, size=None), bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y')])
Farcical answered 19/3, 2021 at 6:17 Comment(3)
This one should be the accepted answer. Thank you so much! I spent an hour searching for a solution that plays nicely with type-annotated classes.Periscope
While this works if you know the structure of the dict, you're not able to create a pydantic model dynamically. For that you need to call create_model(__model_name="ModelName", **some_dict). This however only works with the top level, so you have to make it generate pydantic model recursively.. But creating pydantic models is slower than other modules such as named_tuples or dataclasses.Clovis
What is the overhead of pydantic compared to munch compared to namedtuples?Fretful
B
11

x.__dict__.update(d) should do fine.

Biform answered 20/8, 2009 at 11:31 Comment(6)
thanks for your answer, but what is x? the dict or a standard object? could you give me a hint please.Bondy
x is your object. Every object has a dict. By updating the dict of a object you are actually updating the key vars in it.Biform
The bold words are _ _ dict _ _Biform
This won't handle nested dictionaries.Grotto
Not every object has a __dict__: try object().__dict__ and you'll get AttributeError: 'object' object has no attribute '__dict__'Catcall
@WillManley you are right. Any python code can also define such a type using __slots__.Jenevajeni
A
9

Typically you want to mirror dict hierarchy into your object but not list or tuples which are typically at lowest level. So this is how I did this:

class defDictToObject(object):

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

So we do:

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

and get:

x.b.b1.x 1

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

Adala answered 20/11, 2019 at 9:16 Comment(0)
F
8

This should get your started:

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

It doesn't work for lists, yet. You'll have to wrap the lists in a UserList and overload __getitem__ to wrap dicts.

Feign answered 20/8, 2009 at 11:34 Comment(1)
To make it work for lists, use the if isinstance(d, list) clause from Anon's answer.Malebranche
S
8

I know there's already a lot of answers here already and I'm late to the party but this method will recursively and 'in place' convert a dictionary to an object-like structure... Works in 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
Striation answered 18/10, 2017 at 14:52 Comment(2)
What do you mean "object-like structure"?Erfurt
Object-like because of the duck typing principle. What I mean is you can access its properties like you would if it was an instance of a class. but it's NOT an object in that sense, it's a named tuple.Striation
W
6
from mock import Mock
d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
my_data = Mock(**d)

# We got
# my_data.a == 1
Woodbridge answered 21/8, 2013 at 12:15 Comment(2)
ModuleNotFoundError: No module named 'mock'.Jenevajeni
mock was a third party package, now incorporated into unittest. from unittest.mock import Mock should work. One downside is, if your code is production code, it now depends on what should theoretically be just test code. Another is, this only works for shallow dictionaries. But, at the top level, a nested dictionary gets returned correctly, unlike many of the answers above.Codicil
B
6

This also works well too

class DObj(object):
    pass

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

print dobj.a
>>> aaa
print dobj.b
>>> bbb
Buonaparte answered 8/8, 2017 at 8:0 Comment(2)
Thanks for the answer. You have a typo on line 4, it should be: dobj = DObj(). When trying to run this code in another env I get this error: TypeError: 'DObj' object is not subscriptable , do you know a fix for this? :)Cucurbit
Nvm, I found the cause, my dictionary is not flat.Cucurbit
R
6

The simplest way would be using collections.namedtuple.

I find the following 4-liner the most beautiful, which supports nested dictionaries:

def dict_to_namedtuple(typename, data):
    return namedtuple(typename, data.keys())(
        *(dict_to_namedtuple(typename + '_' + k, v) if isinstance(v, dict) else v for k, v in data.items())
    )

The output will look good as well:

>>> nt = dict_to_namedtuple('config', {
...     'path': '/app',
...     'debug': {'level': 'error', 'stream': 'stdout'}
... })

>>> print(nt)
config(path='/app', debug=config_debug(level='error', stream='stdout'))

>>> print(nt.debug.level)
'error'
Requirement answered 24/4, 2020 at 15:9 Comment(0)
S
4

Let me explain a solution I almost used some time ago. But first, the reason I did not is illustrated by the fact that the following code:

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

print x.from

gives this error:

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

Because "from" is a Python keyword there are certain dictionary keys you cannot allow.


Now my solution allows access to the dictionary items by using their names directly. But it also allows you to use "dictionary semantics". Here is the code with example usage:

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"
Scut answered 20/8, 2009 at 11:52 Comment(1)
class Struct: def init__(self, **entries): self.__dict.update(entries)Veridical
P
4

Old Q&A, but I get something more to talk. Seems no one talk about recursive dict. This is my 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()
Pullulate answered 13/6, 2012 at 2:23 Comment(0)
E
4

Wanted to upload my version of this little paradigm.

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)

It preserves the attributes for the type that's imported into the class. My only concern would be overwriting methods from within the dictionary your parsing. But otherwise seems solid!

Exedra answered 14/8, 2012 at 22:38 Comment(1)
Although a nice idea, this doesn't seem to work for the OP's example, in fact it seems to modify the passed in dictionary! In fact, df.a doesn't even work.Stillbirth
L
3

Here is another way to implement SilentGhost's original suggestion:

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
Lectionary answered 20/8, 2009 at 14:18 Comment(0)
P
3

I stumbled upon the case I needed to recursively convert a list of dicts to list of objects, so based on Roberto's snippet here what did the work for 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

Note that any tuple will be converted to its list equivalent, for obvious reasons.

Hope this helps someone as much as all your answers did for me, guys.

Popcorn answered 24/4, 2011 at 16:41 Comment(0)
V
3

What about just assigning your dict to the __dict__ of an empty object?

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

Of course this fails on your nested dict example unless you walk the dict recursively:

# 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

And your example list element was probably meant to be a Mapping, a list of (key, value) pairs like this:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': [("hi", {'foo': "bar"})]}
>>> obj = dict2obj(d)
>>> obj.d.hi.foo
"bar"
Vision answered 14/5, 2016 at 20:40 Comment(0)
S
2

Here's another implementation:

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] Missed bit about also handling dicts within lists, not just other dicts. Added fix.

Standstill answered 20/8, 2009 at 13:33 Comment(3)
Note that setting dict to the source dictionary means that any changes to attributes on the resulting object will also affect the dictionary that created the object, and vice-versa. This may lead to unexpected results if the dictionary is used for something other than creating the object.Hiramhirasuna
@Mark: Actually, a new dictionary is being passed to DictObj every time, rather than just passing through the same dict object, so this won't actually occur. It's neccessary to do this, as I need to translate the values within a dictionary as well, so it would be impossible to pass through the original dict object without mutating it myself.Standstill
There are quite a few answers to this, the accepted answer didn't look like it would be recursive or handle lists. I read through them all and this one looked the simplest at first sight, I tested and it worked. Great answer.Synge
P
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
Photodrama answered 18/6, 2012 at 22:26 Comment(0)
Q
2

How about this:

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

This can then be used like this:

>>> o=d2o({"a" : 5, "b" : 3})
>>> print o.a
5
>>> print o.b
3
Queenhood answered 4/5, 2013 at 8:58 Comment(1)
This doesn't answer the question. The author want to convert nested dict. Try {'a': 1, 'b': {'b1': 2}} and you will get AttributeErrorEx
I
2

I think a dict consists of number, string and dict is enough most time. So I ignore the situation that tuples, lists and other types not appearing in the final dimension of a dict.

Considering inheritance, combined with recursion, it solves the print problem conveniently and also provides two ways to query a data,one way to edit a data.

See the example below, a dict that describes some information about students:

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)

Results:

{'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': ''}
Impala answered 7/10, 2013 at 6:47 Comment(0)
J
2
class Dict2Obj:
    def __init__(self, json_data):
        self.convert(json_data)

    def convert(self, json_data):
        if not isinstance(json_data, dict):
            return
        for key in json_data:
            if not isinstance(json_data[key], dict):
                self.__dict__.update({key: json_data[key]})
            else:
                self.__dict__.update({ key: Dict2Obj(json_data[key])})

I could not find the implementation of nested dictionary to object, so wrote one.

Usage:

>>> json_data = {"a": {"b": 2}, "c": 3}
>>> out_obj = Dict2Obj(json_data)
>>> out_obj.a
<Dict2Obj object at 0x7f3dc22c2d68>
>>> out_obj.a.b
2
>>> out_obj.a.c
3
Jamima answered 19/5, 2021 at 17:41 Comment(0)
H
2

Looking for a simple wrapper class for dict enabling attribute-style key access/assignment (dot notation) I was not satisfied with the existing options for the reasons below.

dataclasses, pydantic, etc. are great but require a static definition of the content. Also, they cannot replace dict in code which relied on dict since they don't share the same methods and __getitem__() syntax is not supported.

Hence, I developed MetaDict. It behaves exactly like dict but enables dot notation and IDE autocompletion (if the object is loaded in the RAM) without the shortcomings and potential namespace conflicts of other solutions. All features and usage examples can be found on GitHub (see link above).

Full disclosure: I am the author of MetaDict.

Shortcomings/limitations I encountered when trying out other solutions:

  • Addict
    • No key autocompletion in IDE
    • Nested key assignment cannot be turned off
    • Newly assigned dict objects are not converted to support attribute-style key access
    • Shadows inbuilt type Dict
  • Prodict
    • No key autocompletion in IDE without defining a static schema (similar to dataclass)
    • No recursive conversion of dict objects when embedded in list or other inbuilt iterables
  • AttrDict
    • No key autocompletion in IDE
    • Converts list objects to tuple behind the scenes
  • Munch
    • Inbuilt methods like items(), update(), etc. can be overwritten with obj.items = [1, 2, 3]
    • No recursive conversion of dict objects when embedded in list or other inbuilt iterables
  • EasyDict
    • Only strings are valid keys, but dict accepts all hashable objects as keys
    • Inbuilt methods like items(), update(), etc. can be overwritten with obj.items = [1, 2, 3]
    • Inbuilt methods don't behave as expected: obj.pop('unknown_key', None) raises an AttributeError

Note: I wrote a similar answer in this stackoverflow, which is related.

Hysteresis answered 9/2, 2022 at 8:27 Comment(0)
S
1

Building off my answer to "python: How to add property to a class dynamically?":

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

You call makedata on the dictionary you want converted, or maybe trydata depending on what you expect as input, and it spits out a data object.

Notes:

  • You can add elifs to trydata if you need more functionality.
  • Obviously this won't work if you want x.a = {} or similar.
  • If you want a readonly version, use the class data from the original answer.
Setup answered 26/8, 2009 at 13:48 Comment(0)
R
1

My dictionary is of this 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]'}
    ]
}

As can be seen, I have nested dictionaries and list of dicts. This is because the addr_bk was decoded from protocol buffer data that converted to a python dict using lwpb.codec. There are optional field (e.g. email => where key may be unavailable) and repeated field (e.g. phone => converted to list of dict).

I tried all the above proposed solutions. Some doesn't handle the nested dictionaries well. Others cannot print the object details easily.

Only the solution, dict2obj(dict) by Dawie Strauss, works best.

I have enhanced it a little to handle when the key cannot be found:

# 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

Then, I tested it with:

# 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
Richela answered 8/1, 2013 at 5:40 Comment(0)
L
1

Here is a nested-ready version with 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'
Lambskin answered 25/8, 2015 at 17:6 Comment(0)
M
1

Convert dict to 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)

Reference Answer

Malony answered 14/6, 2019 at 10:35 Comment(0)
P
1

I wasn't satisfied with the marked and upvoted answers, so here is a simple and general solution for transforming JSON-style nested datastructures (made of dicts and lists) into hierachies of plain objects:

# tested in: Python 3.8
from collections import abc
from typings import Any, Iterable, Mapping, Union

class DataObject:
    def __repr__(self):
        return str({k: v for k, v in vars(self).items()})

def data_to_object(data: Union[Mapping[str, Any], Iterable]) -> object:
    """
    Example
    -------
    >>> data = {
    ...     "name": "Bob Howard",
    ...     "positions": [{"department": "ER", "manager_id": 13}],
    ... }
    ... data_to_object(data).positions[0].manager_id
    13
    """
    if isinstance(data, abc.Mapping):
        r = DataObject()
        for k, v in data.items():
            if type(v) is dict or type(v) is list:
                setattr(r, k, data_to_object(v))
            else:
                setattr(r, k, v)
        return r
    elif isinstance(data, abc.Iterable):
        return [data_to_object(e) for e in data]
    else:
        return data
Painter answered 14/2, 2020 at 13:43 Comment(0)
S
1

Building on what was done earlier by the accepted answer, if you would like to have it recursive.

class FullStruct:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            if isinstance(value, dict):
                f = FullStruct(**value)
                self.__dict__.update({key: f})
            else:
                self.__dict__.update({key: value})
Soporific answered 9/6, 2020 at 15:3 Comment(0)
I
1

You can use my way to handle it.

somedict= {"person": {"name": "daniel"}}

class convertor:
    def __init__(self, dic: dict) -> object:
        self.dict = dic

        def recursive_check(obj):
            for key, value in dic.items():
                if isinstance(value, dict):
                    value= convertor(value)
                setattr(obj, key, value)
        recursive_check(self)
my_object= convertor(somedict)

print(my_object.person.name)
Interbrain answered 30/1, 2022 at 21:10 Comment(0)
P
1

The following code from here, works on nested dictionaries and IDEs such as VS Code are able to hint the existing attributes:

class Struct:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            if isinstance(value, dict):
                self.__dict__[key] = Struct(**value)
            else:
                self.__dict__[key] = value


my_dict = {
    'name': 'bobbyhadz',
    'address': {
        'country': 'Country A',
        'city': 'City A',
        'codes': [1, 2, 3]
    },
}

obj = Struct(**my_dict)

If you want to see how to load a YAML file and covert it to a Python object, see this gist.

Plus answered 21/12, 2022 at 19:50 Comment(1)
In general I like this solution because it's pretty concise. But, as with many of the other solutions, intermediate dictionaries become just an instance of the proxy (i.e. my_dict.address is no longer a dictionary, but an instance of Struct).Codicil
D
0

I had some problems with __getattr__ not being called so I constructed a new style class version:

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

This version also has the addition of NoneStruct that is returned when an attribute is called that is not set. This allows for None testing to see if an attribute is present. Very usefull when the exact dict input is not known (settings etc.).

bla = Struct({'a':{'b':1}})
print(bla.a.b)
>> 1
print(bla.a.c == None)
>> True
Deponent answered 22/8, 2012 at 14:44 Comment(0)
B
0

This is another, alternative, way to convert a list of dictionaries to an object:

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)
Buchmanism answered 22/10, 2013 at 7:6 Comment(0)
I
0

This little class never gives me any problem, just extend it and use the copy() method:

  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);
Illuminance answered 7/11, 2014 at 15:51 Comment(0)
M
0

Updated with recursive array expansion on @max-sirwa 's code

class Objectify:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            if isinstance(value, dict):
                f = Objectify(**value)
                self.__dict__.update({key: f})
            elif isinstance(value, list):
                t = []
                for i in value:
                    t.append(Objectify(**i)) if isinstance(i, dict) else t.append(i)
                self.__dict__.update({key: t})
            else:
                self.__dict__.update({key: value})
Mozambique answered 17/8, 2020 at 8:30 Comment(0)
S
0

The exact solution of the question can be achieved easily by PyPI package named attrdict. The interesting fact about this package is that the dict can be accessed either as keys or as attributes. Here is the solution -

from attrdict import AttrDict

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

x = AttrDict(d)

print(x.a, x['a'])
print(x.b.c, x['b']['c'])
print(x.d[1].foo, x['d'][1]['foo'])

And output is as follows (obviously with no error) -

1 1
2 2
bar bar

N.B. It was first released in Feb 2, 2019 which means at the time of asking this question, this third party pypi package didn't exist. But if someone now wants to access dict value either by key or by attribute, this package surely can help as magic with only one line of code.

Safekeeping answered 22/12, 2022 at 6:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.