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

时间:2016-10-26 23:49:58

标签: python list dictionary setter

我有一个我无法弄清楚的问题。假设你有一个类,这个类接受嵌套字典列表作为输入。它使用新的空字典初始化每个字典中的一个键。稍后,我想将列表中某个对象的嵌套值设置为某个东西。出于某种原因,这似乎会影响列表中的其他对象吗?

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

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”,例如作为类变量或在类外部。问题仍然存在。

3 个答案:

答案 0 :(得分:3)

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

在第二个例子中:

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

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

答案 1 :(得分:2)

这是因为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

在第二行中,我创建了一个列表,其第0个和第一个元素指向my_dict

在第三行,我访问my_dict(通过my_list[0]),我 mutate :更改与"key"相关联的值。

在第四行中,我检查my_list的值。 my_list的第0和第0个元素仍指向my_dict - 我们已更改my_dict。因此,更改将反映在列表的两个元素中。

解决此问题的一种方法:

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

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

答案 2 :(得分:1)

将密钥分配给inside_init_dict字典的不同副本而不是相同的副本:

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