Python: Easily access deeply nested dict (get and set)

Viewed 24615

I'm building some Python code to read and manipulate deeply nested dicts (ultimately for interacting with JSON services, however it would be great to have for other purposes) I'm looking for a way to easily read/set/update values deep within the dict, without needing a lot of code.

@see also Python: Recursively access dict via attributes as well as index access? -- Curt Hagenlocher's "DotDictify" solution is pretty eloquent. I also like what Ben Alman presents for JavaScript in http://benalman.com/projects/jquery-getobject-plugin/ It would be great to somehow combine the two.

Building off of Curt Hagenlocher and Ben Alman's examples, it would be great in Python to have a capability like:

>>> my_obj = DotDictify()
>>> my_obj.a.b.c = {'d':1, 'e':2}
>>> print my_obj
{'a': {'b': {'c': {'d': 1, 'e': 2}}}}
>>> print my_obj.a.b.c.d
1
>>> print my_obj.a.b.c.x
None
>>> print my_obj.a.b.c.d.x
None
>>> print my_obj.a.b.c.d.x.y.z
None

Any idea if this is possible, and if so, how to go about modifying the DotDictify solution?

Alternatively, the get method could be made to accept a dot notation (and a complementary set method added) however the object notation sure is cleaner.

>>> my_obj = DotDictify()
>>> my_obj.set('a.b.c', {'d':1, 'e':2})
>>> print my_obj
{'a': {'b': {'c': {'d': 1, 'e': 2}}}}
>>> print my_obj.get('a.b.c.d')
1
>>> print my_obj.get('a.b.c.x')
None
>>> print my_obj.get('a.b.c.d.x')
None
>>> print my_obj.get('a.b.c.d.x.y.z')
None

This type of interaction would be great to have for dealing with deeply nested dicts. Does anybody know another strategy (or sample code snippet/library) to try?

4 Answers

The older answers have some pretty good tips in them, but they all require replacing standard Python data structures (dicts, etc.) with custom ones, and would not work with keys that are not valid attribute names.

These days we can do better, using a pure-Python, Python 2/3-compatible library, built for exactly this purpose, called glom. Using your example:

import glom

target = {}  # a plain dictionary we will deeply set on
glom.assign(target, 'a.b.c', {'d': 1, 'e': 2}, missing=dict)
# {'a': {'b': {'c': {'e': 2, 'd': 1}}}}

Notice the missing=dict, used to autocreate dictionaries. We can easily get the value back using glom's deep-get:

glom.glom(target, 'a.b.c.d')
# 1

There's a lot more you can do with glom, especially around deep getting and setting. I should know, since (full disclosure) I created it. That means if you find a gap, you should let me know!

To fellow googlers: we now have addict:

pip install addict

and

mapping.a.b.c.d.e = 2
mapping
{'a': {'b': {'c': {'d': {'e': 2}}}}}

I used it extensively.

To work with dotted paths, I found dotted:

obj = DottedDict({'hello': {'world': {'wide': 'web'}}})
obj['hello.world.wide'] == 'web'  # true
Related