如果我有一个字典,例如
d = { 'a': {
"x": [],
"y": {
"z": {
"1": 'loser'
}
}
}
}
print(d['a']['y']['z']['1']) #=> loser
但是,我不知道此字典中有多少个嵌套项。相反,我有像这样的键列表:
['a', 'y', 'z', '1']
设置d['a']['y']['z']['1'] = 'winner'
的一种优雅方法是什么?
这是我尝试过的:
l = ['a', 'y', 'z', '1']
def change_the_value(d, l, value):
if len(l) == 1:
d[l[0]] = value
if len(l) == 2:
d[l[0]][l[1]] = value
if len(l) == 3:
d[l[0]][l[1]][l[2]] = value
if len(l) == 4:
d[l[0]][l[1]][l[2]][l[3]] = value
# ... ad infinitum
return d
change_the_value(d, l, 'winner')
print(d) # => {'a': {'x': [], 'y': {'z': {'1': 'winner'}}}}
答案 0 :(得分:6)
您可以使用简单的for
循环:
_path = ['a', 'y', 'z', '1']
d = {'a': {'x': [], 'y': {'z': {'1': 'loser'}}}}
_start = d
for i in _path[:-1]:
_start = _start[i]
_start[_path[-1]] = 'winner'
print(d)
输出:
{'a': {'x': [], 'y': {'z': {'1': 'winner'}}}}
您还可以使用递归(如果您不介意为d
创建新结构):
def update(_d, _path):
return {a:'winner' if a == _path[-1] and len(_path) == 1 else
update(b, _path[1:]) if a == _path[0] else b for a, b in _d.items()}
print(update(d, ['a', 'y', 'z', '1']))
输出:
{'a': {'x': [], 'y': {'z': {'1': 'winner'}}}}
答案 1 :(得分:5)
如果可以确定键列表有效,则可以使用functools.reduce
。
>>> from functools import reduce
>>>
>>> keys = ['a', 'y', 'z', '1']
>>> d = { 'a': {
...: "x": [],
...: "y": {
...: "z": {
...: "1": 'loser'
...: }
...: }
...: }
...: }
>>>
>>> the_dict = reduce(dict.get, keys[:-1], d)
>>> the_dict[keys[-1]] = 'winner'
>>> d
{'a': {'x': [], 'y': {'z': {'1': 'winner'}}}}
答案 2 :(得分:3)
无论长度为l
,都应该这样做!
tmp = d
for key in l[:-1]:
tmp = tmp[key]
tmp[l[-1]] = 'winner'
我觉得必须有更好的方法,但这至少消除了所有这些if
语句。
答案 3 :(得分:0)
如果您有按键列表,还会发布其他好的答案。我注意到您的示例正在寻找第一个字符串值。如果始终都是这样,那么以下解决方案可以在没有键列表的情况下工作:
def setval(d, val):
for i in d:
if isinstance(d[i], dict):
return setval(d[i], val)
if isinstance(d[i], str):
d[i] = val
return
return None
setval(d, 'winner')
print(d)
或者,如果您确实想使用列表来指定路径,则可以采用以下递归方法:
def setvalat(d, l, val):
if len(l) > 1:
setvalat(d[l[0]], l[1:], val)
else:
d[l[0]] = val
setvalat(d, ['a', 'y', 'z', '1'], 'winner')
print(d)