递归访问和修改字典
我有一个这样的字典:
my_dict = {'key1': {'key2': {'foo': 'bar'} } }
我想在key1->key2->key3这个路径上添加一个值为'blah'的条目,结果应该是:
my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': 'blah'} } }
我希望找到一个通用的解决方案,这个方案不受键的数量限制,也就是说,key1->key2->key3->key4->key5这样的路径也应该能工作,即使从key3开始往下的键并不存在。这样我就能得到:
my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': {'key4': {'key5': 'blah'} } } } }
提前谢谢你。
2 个回答
1
一个替代Martijn Pieters精彩回答的方法是使用嵌套的defaultdict
,而不是普通的字典:
from collections import defaultdict
nested = lambda: defaultdict(nested) # nested dictionary factory
my_dict = nested()
你可以像使用普通嵌套字典那样设置值,空字典会根据需要自动创建,以填充中间的层级:
my_dict["key1"]["key2"]["key3"] = "blah"
当然,这要求你在写代码设置值时,提前知道键的数量。如果你想处理一个可变长度的键列表,而不是固定数量的键,你就需要一些函数来帮助你获取和设置这些值,就像Martijn的回答中提到的那样。
12
你可以使用 reduce()
函数 来遍历一系列嵌套的字典:
def get_nested(d, path):
return reduce(dict.__getitem__, path, d)
示例:
>>> def get_nested(d, path):
... return reduce(dict.__getitem__, path, d)
...
>>> my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': {'key4': {'key5': 'blah'}}}}}
>>> get_nested(my_dict, ('key1', 'key2', 'key3', 'key4', 'key5'))
'blah'
这个版本在找不到某个键的时候会抛出一个异常:
>>> get_nested(my_dict, ('key1', 'nonesuch'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in get_nested
KeyError: 'nonesuch'
但你可以把 dict.__getitem__
替换成 lambda d, k: d.setdefault(k, {})
,这样就会在找不到键的时候创建空字典:
def get_nested_default(d, path):
return reduce(lambda d, k: d.setdefault(k, {}), path, d)
示例:
>>> def get_nested_default(d, path):
... return reduce(lambda d, k: d.setdefault(k, {}), path, d)
...
>>> get_nested_default(my_dict, ('key1', 'nonesuch'))
{}
>>> my_dict
{'key1': {'key2': {'key3': {'key4': {'key5': 'blah'}}, 'foo': 'bar'}, 'nonesuch': {}}}
要在某个特定路径上 设置 一个值,你需要遍历所有的键,除了最后一个,然后用最后一个键进行普通的字典赋值:
def set_nested(d, path, value):
get_nested_default(d, path[:-1])[path[-1]] = value
这个方法使用了 get_nested_default()
函数,根据需要添加空字典:
>>> def set_nested(d, path, value):
... get_nested_default(d, path[:-1])[path[-1]] = value
...
>>> my_dict = {'key1': {'key2': {'foo': 'bar'}}}
>>> set_nested(my_dict, ('key1', 'key2', 'key3', 'key4', 'key5'), 'blah')
>>> my_dict
{'key1': {'key2': {'key3': {'key4': {'key5': 'blah'}}, 'foo': 'bar'}}}