nested dict access via a pathing system
aka: being able to access a nested item via an xpath-esque syntax
>>> from addict import Dict
>>> a = Dict()
>>> a.b.c = 10
>>> a.b.d = 20
>>> a.c.c = 100
>>> a.c.d = 200
>>>
>>> a.search('a/b/c') # or a.search('a.b.c')
10
>>> a.search('a/b').c = 11
>>> a.b.c
11
>>> a.search('a/b/[cd]')
(11, 20)
>>> a.search('a/b/[cd]', full_path=True)
{a: {b: {c: 11, d: 20}}}
>>> a.search('a/*/c', full_path=True)
{a: {b: {c: 11}, c: {c: 100}}
There's a lot more that could be expanded upon this. This is basically like the dpath library or the jsonpointer library but integrated into the library.
I think this is a good opportunity for a kind of superdict as this as it implements a lot of possibilities. It could be as simple as binding the dpath functions internally making that lib a requirement (eh), or maybe creating an interface akin to that.
However, I realize this is a lot to request (hehe) and probably is more work than warranted (I could try to take a stab at it potentially). Another less extreme idea is just to add simple iterable get and set. ex:
>>> from addict import Dict
>>> a = Dict()
>>> a.b.c = 10
>>> a.get_from(['a', 'b', 'c'])
10
>>> a.set_from(['a', 'b', 'c'], 20)
>>> a.b.c
20
>>> # this functionality can easily be replicated but it uses eval so it's not favorable
>>> path = '.'.join(['a', 'b', 'c'])
>>> eval(f'a.{path}') # yuck
20
These are just a couple ideas I had. I think the first idea might be taking it a lil too far but the latter does fit in with the rest of this class I feel. Just wondering what you thought. I potentially may come with a pull request if I find the time.
Thanks! ~ Ritik Shah ~
Hi @RitikShah, thanks for your contribution!
I'd be open to including the latter functionality. Keen on making a PR?
mewwts, thanks for creating addict - we use it extensively for a number of our internal apps and modules.
Just in case anyone comes across this and needs an immediate solution, here is a quick and dirty func I wrote that can walk a dotted-string notation to set/get a Dict() property. Not extensively tested and only handles dictionaries and absolute paths. If time permits I'll try to expand upon this and possibly submit a PR.
def tree(obj, path, data=None, separator="."):
""" String to addict nested property conversion """
# Extract branch/leaf properties from path
tree = path.split(separator)
leaf = tree.pop()
# Loop through branches to get to leaf, creating branches as needed
for branch in tree:
obj = getattr(obj, branch, obj[branch])
# At the leaf, assign data if set
if data:
obj[leaf] = data
# Return leaf
return obj[leaf]
Usage:
test = Dict()
tree(test, "foo.bar.baz", 42)
>>> 42
test
>>> {'foo': {'bar': {'baz': 42}}}
tree(test, "foo.bar.baz")
>>> 42
tree(test, "baz.bar.foo")
>>> {}