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?
This question is related to
python
object
dictionary
x.__dict__.update(d)
should do fine.
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
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': ''}
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.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()))
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
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
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:
trydata
if you need more functionality.x.a = {}
or similar.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
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']
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
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'}}
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
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
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'
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.
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
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
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
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"
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})
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'
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()))
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'
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.
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
from mock import Mock
d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
my_data = Mock(**d)
# We got
# my_data.a == 1
>>> 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'
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})
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"
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!
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);
# 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.
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.
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.
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'
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()
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)
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)
Source: Stackoverflow.com