如何使用给定的列表位置设置嵌套 python dict 的“第 n 个”元素?

Posted

技术标签:

【中文标题】如何使用给定的列表位置设置嵌套 python dict 的“第 n 个”元素?【英文标题】:How to set "nth" element of a nested python dict with given list location? 【发布时间】:2019-05-16 18:38:33 【问题描述】:

例如,如果我有一个字典

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'

【问题讨论】:

【参考方案1】:

您可以使用简单的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'

【讨论】:

感谢您分享您的知识。我从这样的东西开始,但无法完成它,因为我没有理解字典的副本是如何修改相同的内存空间的。 @Conner 实际上从_start 中引用了d。因此,_start 的任何突变也会出现在d 中。【参考方案2】:

如果您可以确定密钥列表有效,则可以使用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'

【讨论】:

【参考方案3】:

对于l 的任何长度都应该这样做!

tmp = d
for key in l[:-1]:
    tmp = tmp[key]

tmp[l[-1]] = 'winner'

我觉得肯定有更好的方法,但这至少消除了所有那些if 声明。

【讨论】:

不,没关系。 +1 我确实更喜欢@timgeb 的reduce 版本,因为,嘿,它是单行的!与我的 Python 级 for 循环相比,它也可能更快,具体取决于 reduce 如何实现其内部循环。我喜欢我的更明显,因为即使我经常使用functools,我也必须查找reduce 的签名。两者显然都有优点!【参考方案4】:

如果您有密钥列表,还会发布其他好的答案。我注意到您的示例正在寻找第一个字符串值。如果这总是正确的,这里有一个解决方案,可以在没有键列表的情况下工作:

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)

【讨论】:

以上是关于如何使用给定的列表位置设置嵌套 python dict 的“第 n 个”元素?的主要内容,如果未能解决你的问题,请参考以下文章

Python:使用索引展平嵌套列表

Python:如何访问返回值以放入表中?

p2·python中嵌套列表list元素输出·模块封装·发布上传(pigeon详细说)

我需要比较python中的2个嵌套列表以查找第一个列表的字符串与第二个列表中的字符串不匹配的位置

如何根据特定的值进行聚合,并创建给定对象列表的嵌套地图?

python - 在给定条件下查找列表中下一项的位置