This is a very interesting and a very practical situation one can encounter.
There are numerous implementations, each of which solve certain problems but miss out on a few edge cases.
Possible solutions and varying answers can be found in these titles.
What is the best way to implement nested dictionaries?
What's the best way to initialize a dict of dicts in Python?
Set nested dict value and create intermediate keys
Also, there are numerous gists and blogs found on this requirement 'autovivification', including a wikipedia presence.
http://blog.yjl.im/2013/08/autovivification-in-python.html
https://news.ycombinator.com/item?id=3881171
https://gist.github.com/hrldcpr/2012250
https://en.wikipedia.org/wiki/Autovivification
http://blogs.fluidinfo.com/terry/2012/05/26/autovivification-in-python-nested-defaultdicts-with-a-specific-final-type/
While the above implementations are handy once, edge cases can still be problematic. At the time of this writing, no implementation has handled well whether there is a primitive sitting and blocking the nest.
Here are the 3 main ways this question and related questions are answered here in StackOverflow.
Write a helper method, that accepts a dictionary, value and list of nested keys.
Works well with plain dict objects, but lacks the usual square bracket syntax.
Use Defaultdict and write a custom class. Fundamentally this works since default dict supplies {} for missing keys.
Great syntax, but works only for the objects that were created using the custom class.
Use tuples to store and retrieve (https://stackoverflow.com/a/651930/968442).
The Worst idea of all. Should not even be claimed as a solution. Here is why
mydict = {}
mydict['foo', 'bar', 'baz'] = 1
print mydict['foo', 'bar', 'baz']
Will work fine, but when you access mydict['foo', 'bar']
the expectation will be {'baz':1}
, not a KeyError
.
This basically destroys the idea of iterable & nested structure.
Of the three approaches, my bet goes to option 1. By writing a tiny helper method the edge cases can be resolved pragmatically, here is my implementation.
def sattr(d, *attrs):
# Adds "val" to dict in the hierarchy mentioned via *attrs
for attr in attrs[:-2]:
# If such key is not found or the value is primitive supply an empty dict
if d.get(attr) is None or not isinstance(d.get(attr), dict):
d[attr] = {}
d = d[attr]
d[attrs[-2]] = attrs[-1]
Now
my_pets = {'Rudolf': {'animal': 'cat', 'legs': 4}}
sattr(my_pets, 'Rudolf', 'legs', 'front-right', 'injured', True)
sattr(my_pets, 'Rudolf', 'legs', 'front-left', 'injured', False)
will produce
{'Rudolf': {'animal': 'cat', 'legs': 4}}
{'Rudolf': {'animal': 'cat', 'legs': {'front-right': {'injured': True}}}}
{'Rudolf': {'animal': 'cat', 'legs': {'front-right': {'injured': True}, 'front-left': {'injured': False}}}}