41

我正在尝试以编程方式在字典中设置一个值,可能是嵌套的,给定一个索引列表和一个值。

例如,假设我的索引列表是:

['person', 'address', 'city']

值是

'New York'

因此,我想要一个字典对象,例如:

{ 'Person': { 'address': { 'city': 'New York' } }

基本上,该列表表示嵌套字典的“路径”。

我想我可以自己构建字典,但我遇到的问题是如何设置值。显然,如果我只是手动为此编写代码,它将是:

dict['Person']['address']['city'] = 'New York'

但是,如果我只有索引和值的列表,我如何索引到字典中并以编程方式设置值?

Python

4

8 回答 8

68

这样的事情可能会有所帮助:

def nested_set(dic, keys, value):
    for key in keys[:-1]:
        dic = dic.setdefault(key, {})
    dic[keys[-1]] = value

你可以像这样使用它:

>>> d = {}
>>> nested_set(d, ['person', 'address', 'city'], 'New York')
>>> d
{'person': {'address': {'city': 'New York'}}}
于 2012-12-03T17:00:42.050 回答
5

我从Bakuriu 的答案中自由地扩展了代码。因此,对此的支持是可选的,因为他的代码本身就是一个机智的解决方案,这是我没有想到的。

def nested_set(dic, keys, value, create_missing=True):
    d = dic
    for key in keys[:-1]:
        if key in d:
            d = d[key]
        elif create_missing:
            d = d.setdefault(key, {})
        else:
            return dic
    if keys[-1] in d or create_missing:
        d[keys[-1]] = value
    return dic

设置create_missing为 True 时,您确保只设置已经存在的值:

# Trying to set a value of a nonexistent key DOES NOT create a new value
print(nested_set({"A": {"B": 1}}, ["A", "8"], 2, False))
>>> {'A': {'B': 1}}

# Trying to set a value of an existent key DOES create a new value
print(nested_set({"A": {"B": 1}}, ["A", "8"], 2, True))
>>> {'A': {'B': 1, '8': 2}}

# Set the value of an existing key
print(nested_set({"A": {"B": 1}}, ["A", "B"], 2))
>>> {'A': {'B': 2}}
于 2018-03-15T02:45:40.287 回答
3

首先,您可能想查看setdefault

作为一个函数,我将它写为

def get_leaf_dict(dct, key_list):
    res=dct
    for key in key_list:
        res=res.setdefault(key, {})
    return res

这将用作:

get_leaf_dict( dict, ['Person', 'address', 'city']) = 'New York'

这可以通过错误处理等来清理。也使用*args而不是单个键列表参数可能会很好;但想法是您可以遍历键,在每个级别提取适当的字典。

于 2012-12-03T17:00:33.003 回答
2

这是另一种选择:

from collections import defaultdict
recursivedict = lambda: defaultdict(recursivedict)
mydict = recursivedict()

我最初是从这里得到的:设置嵌套字典值并创建中间键

如果你问我,这是非常聪明和优雅的。

于 2014-07-11T01:13:59.560 回答
2

这是我的简单解决方案:只需写

terms = ['person', 'address', 'city'] 
result = nested_dict(3, str)
result[terms] = 'New York'  # as easy as it can be

你甚至可以这样做:

terms = ['John', 'Tinkoff', '1094535332']  # account in Tinkoff Bank
result = nested_dict(3, float)
result[terms] += 2375.30

现在后台:

from collections import defaultdict


class nesteddict(defaultdict):
    def __getitem__(self, key):
        if isinstance(key, list):
            d = self
            for i in key:
                d = defaultdict.__getitem__(d, i)
            return d
        else:
            return defaultdict.__getitem__(self, key)
    def __setitem__(self, key, value):
        if isinstance(key, list):
            d = self[key[:-1]]
            defaultdict.__setitem__(d, key[-1], value)
        else:
            defaultdict.__setitem__(self, key, value)


def nested_dict(n, type):
    if n == 1:
        return nesteddict(type)
    else:
        return nesteddict(lambda: nested_dict(n-1, type))
于 2016-11-01T12:38:37.093 回答
2

Python 3的dotty_dict库可以做到这一点。请参阅文档Dotty Dict以获得更清晰的信息。

from dotty_dict import dotty

dot = dotty()
string = '.'.join(['person', 'address', 'city'])
dot[string] = 'New York'

print(dot)

输出:

{'person': {'address': {'city': 'New York'}}}
于 2020-01-24T15:50:12.820 回答
1

这是Bakuriu 答案的一个变体,它不依赖于单独的函数:

keys = ['Person', 'address', 'city']
value = 'New York'

nested_dict = {}

# Build nested dictionary up until 2nd to last key
# (Effectively nested_dict['Person']['address'] = {})
sub_dict = nested_dict
for key_ind, key in enumerate(keys[:-1]):
    if not key_ind:
        # Point to newly added piece of dictionary
        sub_dict = nested_dict.setdefault(key, {})
    else:
        # Point to newly added piece of sub-dictionary
        # that is also added to original dictionary
        sub_dict = sub_dict.setdefault(key, {})
# Add value to last key of nested structure of keys
# (Effectively nested_dict['Person']['address']['city'] = value)
sub_dict[keys[-1]] = value

print(nested_dict)

>>> {'Person': {'address': {'city': 'New York'}}}
于 2019-05-30T20:56:29.940 回答
1

使用这对方法

def gattr(d, *attrs):
    """
    This method receives a dict and list of attributes to return the innermost value of the give dict
    """
    try:
        for at in attrs:
            d = d[at]
        return d
    except:
        return None


def sattr(d, *attrs):
    """
    Adds "val" to dict in the hierarchy mentioned via *attrs
    For ex:
    sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
    This method creates necessary objects until it reaches the final depth
    This behaviour is also known as autovivification and plenty of implementation are around
    This implementation addresses the corner case of replacing existing primitives
    https://gist.github.com/hrldcpr/2012250#gistcomment-1779319
    """
    for attr in attrs[:-2]:
        # If such key is not found or the value is primitive supply an empty dict
        if d.get(attr) is None or isinstance(d.get(attr), dict):
            d[attr] = {}
        d = d[attr]
    d[attrs[-2]] = attrs[-1]
于 2018-12-03T01:19:32.040 回答