代码之家  ›  专栏  ›  技术社区  ›  Conner

如何设置具有给定列表位置的嵌套python dict的“nth”元素?

  •  6
  • Conner  · 技术社区  · 6 年前

    例如,如果我有口述

    d = { 'a': {
                "x": [],
                "y": {
                    "z": {
                        "1": 'loser'
                            }
                     }
                }
        }
    
    print(d['a']['y']['z']['1']) #=> loser
    

    但是,我不知道此dict中有多少嵌套项。相反,我有一个键列表,如:

    ['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'}}}}
    
    4 回复  |  直到 6 年前
        1
  •  6
  •   Ajax1234    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'
    
        2
  •  5
  •   timgeb    6 年前

    如果可以确保密钥列表有效,则可以使用 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
  •  3
  •   Scott Colby    6 年前

    这应该可以做到,任何长度 l !

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

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

        4
  •  0
  •   ic3b3rg    6 年前

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

    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)