Python: 在 class 个实例中设置嵌套字典列表的值

Python: setting values of a list of nested dictionaries in class instances

我有一个我无法弄清楚的紧迫问题。假设您有一个 class,这个 class 将嵌套字典列表作为输入。它用一个新的空字典初始化每个字典的一个键。后来,我想将列表中对象的嵌套值之一设置为某物。出于某种原因,这似乎会影响列表中的其他对象?

我知道这听起来很复杂,所以这里有一个例子:

class Tester():
    def __init__(self, stuff):

        # stuff is a list of dictionaries
        self.stuff = stuff

        # Each item in the list should be initialized to this dictionary
        inside_init_dict = {'test': True}
        for x in self.stuff:
            x['info'] = inside_init_dict

if __name__ == '__main__':
    new_stuff = [{'info': {}}, {'info': {}}, {'info': {}}]
    mytest = Tester(new_stuff)

    print(mytest.stuff)
    # >>> [{'info': {'test': True}}, {'info': {'test': True}}, {'info': {'test': True}}]

    # I want to just set a value in the nested dict of the first item in the list
    mytest.stuff[0]['info']['test'] = False

    # However, all items in the list change
    print(mytest.stuff)
    # >>> [{'info': {'test': False}}, {'info': {'test': False}}, {'info': {'test': False}}]

这发生在 Python 2 和 3 上。我能解决这个问题的唯一方法是不使用单独的变量 "inside_init_dict",直接设置初始化字典:

class Tester():
    def __init__(self, stuff):

        # stuff is a list of dictionaries
        self.stuff = stuff

        # Each item in the list should be initialized to this dictionary
        for x in self.stuff:
            x['info'] = {'test': True}

if __name__ == '__main__':
    new_stuff = [{'info': {}}, {'info': {}}, {'info': {}}]
    mytest = Tester(new_stuff)

    print(mytest.stuff)
    # >>> [{'info': {'test': True}}, {'info': {'test': True}}, {'info': {'test': True}}]

    mytest.stuff[0]['info']['test'] = False

    # This is what I want
    print(mytest.stuff)
    # >>> [{'info': {'test': False}}, {'info': {'test': True}}, {'info': {'test': True}}]

这是怎么回事?我试过在不同的地方设置变量 "inside_init_dict",比如作为 class 变量或在 class 之外。问题仍然存在。

将键分配给 inside_init_dict 字典的不同副本,而不是同一字典:

    ...
    inside_init_dict = {'test': True}
    for x in self.stuff:
        x['info'] = inside_init_dict.copy()

在第一个示例中,您在循环外创建了一个字典 inside_init_dict 并将其放在多个位置。列表中的每个元素都得到相同的 inside_init_dict。您看到的不是列表中的其他对象受到影响,只是一个对象被多次显示。

在第二个例子中:

    for x in self.stuff:
        x['info'] = {'test': True}

现在每个 x 都有自己的字典。它们最初都具有相同的值,但它们是不同的实例,就像同卵双胞胎。

发生这种情况是因为 dictsmutable,这意味着您可以在不更改其身份的情况下更改其内容。这是您所看到的行为的一个更简单的示例:

my_dict = { "key" : "value" }
my_list = [ my_dict, my_dict ]
my_list[0]["key"] = "new_value"
print(my_list) # [ {"key" : "new_value"}, {"key": "new_value"} ]

为什么会这样:

在这段代码的第一行,我创建了一个新字典 {"key" : "value"},并为其分配了名称 my_dict

在第二行,我创建了一个列表,它的第零个和第一个元素 both 指向 my_dict.

在第三行,我访问了my_dict(通过my_list[0]),我改变它:改变与[=18=关联的值].

在第四行,我检查了my_list的值。 my_list 的第零个和第一个元素仍然指向 my_dict -- 我们已经更改了 my_dict。所以变化反映在列表的两个元素中。

一种修复方法:

创建两个具有相同值的字典,而不是指向同一个字典两次:

my_list = [ { "key" : "value" } , { "key" : "value" } ] 
my_list[0]["key"] = "new_value"
print(my_list) # [ {"key" : "new_value"}, {"key": "value"} ]