Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set nested dict value and create intermediate keys

I feel like I saw a way to do this recently. Say I've got an empty dict and I want to set a value in a nested dict inside that empty dict, but obviously that nested dict hasn't been created yet. Is there a 1-line way to create the intermediate keys? This is what I want to do:

mydict = {}
mydict['foo']['bar']['foobar'] = 25

If you execute this code you'll get a KeyError exception for 'foo'. Is there a function to create intermediate keys?

Thanks.

like image 317
skandocious Avatar asked Apr 18 '12 21:04

skandocious


2 Answers

from collections import defaultdict
recursivedict = lambda: defaultdict(recursivedict)
mydict = recursivedict()

When you access mydict['foo'], it sets mydict['foo'] to another recursivedict. It'll actually construct a recursivedict for mydict['foo']['bar']['foobar'] as well, but then it'll get thrown out by assigning that to 25.

like image 140
Danica Avatar answered Oct 17 '22 18:10

Danica


An alternative option - depending on your uses, is to use tuples as keys instead of nested dictionaries:

mydict = {}
mydict['foo', 'bar', 'foobar'] = 25

This will work perfectly well unless you want to get a branch of the tree at any point (you can't get mydict['foo'] in this case).

If you knew how many layers of nesting you want, you could also use functools.partial instead of lambda.

from functools import partial
from collections import defaultdict

tripledict = partial(defaultdict, partial(defaultdict, dict))
mydict = tripledict()
mydict['foo']['bar']['foobar'] = 25

Which some people find more readable, and is faster to create instances of than the equivalent lambda-based solution:

python -m timeit -s "from functools import partial" -s "from collections import defaultdict" -s "tripledefaultdict = partial(defaultdict, partial(defaultdict, dict))" "tripledefaultdict()"
1000000 loops, best of 3: 0.281 usec per loop

python -m timeit -s "from collections import defaultdict" -s "recursivedict = lambda: defaultdict(recursivedict)" "recursivedict()"
1000000 loops, best of 3: 0.446 usec per loop

Although, as always, there is no point optimising until you know there is a bottleneck, so pick what is most useful and readable before what is fastest.

like image 33
Gareth Latty Avatar answered Oct 17 '22 16:10

Gareth Latty