使用列表中的项目更改嵌套字典的字典中的值?

Posted

技术标签:

【中文标题】使用列表中的项目更改嵌套字典的字典中的值?【英文标题】:Change values in dict of nested dicts using items in a list? 【发布时间】:2012-08-08 18:29:13 【问题描述】:

您将如何根据列表的值在嵌套字典的字典中修改/创建键/值,其中列表的最后一项是字典的值,而其余项则保存到其中的键听写? 这将是列表:

list_adddress = [ "key1", "key1.2", "key1.2.1", "value" ]

这只会在解析命令行参数等情况下出现问题。很明显,使用dict_nested["key1"]["key1.2"]["key1.2.1"]["value"] 在脚本中修改/创建这个值会很容易。

这将是一个嵌套的字典:

dict_nested =  
    "key1": 
                "key1.1":  
                            "...": "...",
                ,
                "key1.2":  
                            "key1.2.1": "change_this",
                ,
            ,

    "key2": 
                "...": "..."
            ,

我猜在这种情况下,需要递归函数或列表解析之类的东西。

def ValueModify(list_address, dict_nested):
    ...
    ...
    ValueModify(..., ...)

此外,如果 list_address 中的项目将冷藏到不存在的字典中的键,则应创建它们。

【问题讨论】:

point from a list into a dicitonary variable的可能重复 修改了我的答案以创建一个不存在的密钥。 【参考方案1】:

单线:

keys, (newkey, newvalue) = list_address[:-2], list_address[-2:]
reduce(dict.__getitem__, keys, dict_nested)[newkey] = newvalue

注意:dict.getoperator.getitem 会在这里产生错误的异常。

Joel Cornett's answer 中的显式 for 循环可能更具可读性。

如果你想创建不存在的中间字典:

reduce(lambda d,k: d.setdefault(k, ), keys, dict_nested)[newkey] = newvalue

如果你想覆盖不是字典的现有中间值,例如字符串、整数:

from collections import MutableMapping

def set_value(d, keys, newkey, newvalue, default_factory=dict):
    """
    Equivalent to `reduce(dict.get, keys, d)[newkey] = newvalue`
    if all `keys` exists and corresponding values are of correct type
    """
    for key in keys:
        try:
            val = d[key]
        except KeyError:
            val = d[key] = default_factory()
        else:
            if not isinstance(val, MutableMapping):
                val = d[key] = default_factory()
        d = val
    d[newkey] = newvalue

示例

list_address = ["key1", "key1.2", "key1.2.1", "key1.2.1.1", "value"]
dict_nested = 
    "key1": 
                "key1.1": 
                            "...": "...",
                ,
                "key1.2": 
                            "key1.2.1": "change_this",
                ,
            ,

    "key2": 
                "...": "..."
            ,


set_value(dict_nested, list_address[:-2], *list_address[-2:])
assert reduce(dict.get, list_address[:-1], dict_nested) == list_address[-1]

测试

>>> from collections import OrderedDict
>>> d = OrderedDict()
>>> set_value(d, [], 'a', 1, OrderedDict) # non-existent key
>>> d.items()
[('a', 1)]
>>> set_value(d, 'b', 'a', 2) # non-existent intermediate key
>>> d.items()
[('a', 1), ('b', 'a': 2)]
>>> set_value(d, 'a', 'b', 3) # wrong intermediate type
>>> d.items()
[('a', 'b': 3), ('b', 'a': 2)]
>>> d = 
>>> set_value(d, 'abc', 'd', 4)
>>> reduce(dict.get, 'abcd', d) == d['a']['b']['c']['d'] == 4
True
>>> from collections import defaultdict
>>> autovivify = lambda: defaultdict(autovivify)
>>> d = autovivify()
>>> set_value(d, 'abc', 'd', 4)
>>> reduce(dict.get, 'abcd', d) == d['a']['b']['c']['d'] == 4
True
>>> set_value(1, 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError:
>>> set_value([], 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError:
>>> L = [10]
>>> set_value(L, [0], 2, 3)
>>> L
[2: 3]

【讨论】:

这可能是最好的答案,但如果值为str,例如list_address = ["key1", "key1.2", "key1.2.1", "key1.2.1.1", "value"],则在创建不存在的中间字典时会引发TypeError @XianJacobs:TypeError 是正确的。密钥存在,所以它不应该是 KeyError。 塞巴斯蒂安如果存在应该修改:) @XianJacobs:我添加了覆盖中间非字典的解决方案。 做得很好,单眼线真的让人大开眼界【参考方案2】:
address_list = ["key1", "key1.1", "key1.2", "value"]

def set_value(dict_nested, address_list):
    cur = dict_nested
    for path_item in address_list[:-2]:
        try:
            cur = cur[path_item]
        except KeyError:
            cur = cur[path_item] = 
    cur[address_list[-2]] = address_list[-1]

【讨论】:

【参考方案3】:

我认为这就像你所追求的那样。

def ValueModify(l, d):
  if l[0] not in d:
    d[l[0]] = dict()
  if isinstance(d[l[0]], dict):
    ValueModify(l[1:], d[l[0]])
  else:
    d[l[0]] = l[1]

我正在使用isinstance,这是一种类型检查,通常不是您在 Python 中所做的事情,但它确实按预期设置了值。

-- 已编辑--

如果原始nested_dict 未完全填充,则添加了缺失键检查以设置嵌套值。

【讨论】:

好吧!但如果它不存在,这不会创建密钥 它不会,但它可以被修改为这样做。我会更新的。 hmm 不会创建不存在的中间字典【参考方案4】:

这是一个递归解决方案。

def unravel(d, keys):
    i = keys[0]
    keys = keys[1:]
    tmpDict = d[i]
    if type(tmpDict) != type():
        return tmpDict
    else:
        return unravel(tmpDict, keys)

【讨论】:

【参考方案5】:

插入新的键值对或更新键值对:

import copy
def update_nested_map(d, u, *keys):
    d = copy.deepcopy(d)
    keys = keys[0]
    if len(keys) > 1:
        d[keys[0]] = update_nested_map(d[keys[0]], u, keys[1:])
    else:
        d[keys[0]] = u
    return d

测试:

    >>> d = 'm': 'd': 'v': 'w': 1

    >>> update_nested_map(d, 999, ['m', 'd', 'v', 'w'])
    'm': 'd': 'v': 'w': 999

    >>> update_nested_map(d, 999, ['m', 'd', 'v', 'z'])
    'm': 'd': 'v': 'z': 999, 'w': 1

    >>> update_nested_map(d, 999, ['m', 'd', 'l'])
    'm': 'd': 'v': 'w': 1, 'l': 999

    >>> update_nested_map(d, 999, ['m','d'])
    'm': 'd': 999

【讨论】:

以上是关于使用列表中的项目更改嵌套字典的字典中的值?的主要内容,如果未能解决你的问题,请参考以下文章

如何将嵌套字典列表与它们的值中的公共键相加? [复制]

列表中嵌套字典,根据字典的值排序

如何一次对字典或列表中的所有嵌套字典和列表进行排序?

嵌套字典LINQ中的过滤元素

Python编程入门到实践 - 笔记( 6 章)

访问嵌套在字典中的值