Can I access a nested dict with a list of keys?
What you seem to want to do is define your own class of dictionary that supports this kind of indexing. We can attain a fairly neat syntax by using the fact that when you do d[1, 2, 3]
, Python actually passes the tuple (1, 2, 3)
to __getitem__
.
class NestedDict:
def __init__(self, *args, **kwargs):
self.dict = dict(*args, **kwargs)
def __getitem__(self, keys):
# Allows getting top-level branch when a single key was provided
if not isinstance(keys, tuple):
keys = (keys,)
branch = self.dict
for key in keys:
branch = branch[key]
# If we return a branch, and not a leaf value, we wrap it into a NestedDict
return NestedDict(branch) if isinstance(branch, dict) else branch
def __setitem__(self, keys, value):
# Allows setting top-level item when a single key was provided
if not isinstance(keys, tuple):
keys = (keys,)
branch = self.dict
for key in keys[:-1]:
if not key in branch:
branch[key] = {}
branch = branch[key]
branch[keys[-1]] = value
Here are examples of usage
# Getting an item
my_dict = NestedDict({'a': {'b': 1}})
my_dict['a', 'b'] # 1
# Setting an item
my_dict = NestedDict()
my_dict[1, 2, 3] = 4
my_dict.dict # {1: {2: {3: 4}}}
# You can even get a branch
my_dict[1] # NestedDict({2: {3: 4}})
my_dict[1][2, 3] # 4
You can then make NestedDict
implementation richer by also defining __iter__
, __len__
and __contains__
.
Also, this can be integrated fairly easily in your code since any pre-existing dictionary can be turned into a nested one by doing NestedDict(your_dict)
.
This solution creates another dictionary with same keys and then updates the existing dictionary:
#!/usr/bin/env python
from six.moves import reduce
def update2(input_dictionary, new_value, loc):
"""
Update a dictionary by defining the keys.
Parameters
----------
input_dictionary : dict
new_value : object
loc : iterable
Location
Returns
-------
new_dict : dict
Examples
--------
>>> example = {'a': {'b': 'c'}, '1': {'2': {'3': {'4': '5'}}}}
>>> update2(example, 'new', ('a', 'b'))
{'a': {'b': 'new'}, '1': {'2': {'3': {'4': '5'}}}}
>>> update2(example, 'foo', ('1', '2', '3', '4'))
{'a': {'b': 'new'}, '1': {'2': {'3': {'4': 'foo'}}}}
>>> update2(example, 'bar', ('1', '2'))
{'a': {'b': 'new'}, '1': {'2': 'bar'}}
"""
new_dict = reduce(lambda x, y: {y: x}, reversed(loc), new_value)
input_dictionary.update(new_dict)
return input_dictionary
if __name__ == '__main__':
import doctest
doctest.testmod()
use string, list or tuple for access keys