使用列表中的项目更改嵌套命令中的值?

时间:2021-03-06 18:06:34

How would you modify/create keys/values in a dict of nested dicts based on the values of a list, in which the last item of the list is a value for the dict, and the rest of items reefer to keys within dicts?This would be the list:

如何基于列表的值修改/创建嵌套句中的键/值,其中列表的最后一项是该命令的值,其余的项在句中引用键?这将是列表:

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

This would only be a problem in situations like when parsing command line arguments. It's obvious that modifying/creating this value within a script would be pretty easy using dict_nested["key1"]["key1.2"]["key1.2.1"]["value"].

这只会在解析命令行参数时出现问题。很明显,在一个脚本中修改/创建这个值会非常简单,使用dict_嵌套["key1"]["key1.2"]["key1.2.1"]["value"]。

This would be a nested dict of dicts:

这将是一个嵌套的法令:

dict_nested = {     "key1": {                "key1.1": {                             "...": "...",                },                "key1.2": {                             "key1.2.1": "change_this",                },            },    "key2": {                "...": "..."            },}

I guess that in this case, something like a recursive function or a list comprehension would be required.

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

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

Also, if items in list_address would reefer to keys in non-existing dictionaries, they should be created.

此外,如果list_address中的项将压缩到非现有字典中的键,则应该创建它们。

5 个解决方案

#1


9  

One-liner:

一行程序:

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

Note: dict.get and operator.getitem would produce wrong exceptions here.

注意:dict.get和运营商。getitem在这里会产生错误的异常。

An explicit for-loop as in Joel Cornett's answer might be more readable.

乔尔•科内特(Joel Cornett)的答案中明确的for-loop可能更容易读懂。

If you want to create non-existing intermediate dictionaries:

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

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

If you want to override existing intermediate values that are not dictionaries e.g., strings, integers:

如果您想重写不属于字典的现有中间值,例如字符串、整数:

from collections import MutableMappingdef 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

Example

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]

Tests

>>> 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'] == 4True>>> 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'] == 4True>>> set_value(1, 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAILTraceback (most recent call last):...TypeError:>>> set_value([], 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAILTraceback (most recent call last):...TypeError:>>> L = [10]>>> set_value(L, [0], 2, 3)>>> L[{2: 3}]

#2


3  

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


1  

I think this works like you're after.

我觉得这就像你想要的一样。

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]

I'm using isinstance, which is type checking, and not generally something you do in Python, but it does set the value as expected.

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

-- Edited --

——编辑

Added in the missing key check to set nested values if the original nested_dict is not fully populated.

如果原始nested_dict类型没有完全填充,则添加到缺失的键检查以设置嵌套值。

#4


0  

Here's a recursive solution.

这是一个递归的解决方案。

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


0  

To insert a new key-value pair or update the value of a pair:

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

import copydef 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

test:

测试:

    >>> 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}}

#1


9  

One-liner:

一行程序:

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

Note: dict.get and operator.getitem would produce wrong exceptions here.

注意:dict.get和运营商。getitem在这里会产生错误的异常。

An explicit for-loop as in Joel Cornett's answer might be more readable.

乔尔•科内特(Joel Cornett)的答案中明确的for-loop可能更容易读懂。

If you want to create non-existing intermediate dictionaries:

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

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

If you want to override existing intermediate values that are not dictionaries e.g., strings, integers:

如果您想重写不属于字典的现有中间值,例如字符串、整数:

from collections import MutableMappingdef 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

Example

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]

Tests

>>> 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'] == 4True>>> 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'] == 4True>>> set_value(1, 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAILTraceback (most recent call last):...TypeError:>>> set_value([], 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAILTraceback (most recent call last):...TypeError:>>> L = [10]>>> set_value(L, [0], 2, 3)>>> L[{2: 3}]

#2


3  

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


1  

I think this works like you're after.

我觉得这就像你想要的一样。

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]

I'm using isinstance, which is type checking, and not generally something you do in Python, but it does set the value as expected.

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

-- Edited --

——编辑

Added in the missing key check to set nested values if the original nested_dict is not fully populated.

如果原始nested_dict类型没有完全填充,则添加到缺失的键检查以设置嵌套值。

#4


0  

Here's a recursive solution.

这是一个递归的解决方案。

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


0  

To insert a new key-value pair or update the value of a pair:

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

import copydef 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

test:

测试:

    >>> 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}}