简体   繁体   English

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

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

I have a burning question that I just can't figure out. 我有一个亟待解决的问题,我只是想不通。 Let's say you have a class, and this class takes in as input a list of nested dictionaries. 假设您有一个类,该类将嵌套字典的列表作为输入。 It initializes one of the keys of each of these dictionaries with a new, empty dictionary. 它将使用新的空字典初始化每个词典的键之一。 Later, I want to set one of the nested values of an object in the list as something. 稍后,我想将列表中对象的嵌套值之一设置为某种值。 For some reason this seems to affect other objects in the list? 由于某种原因,这似乎会影响列表中的其他对象?

I know that's sounds very convoluted, so here's an example: 我知道这听起来很令人费解,所以这里有个例子:

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

This happens on both Python 2 and 3. The only way I can get around this is to not use the separate variable "inside_init_dict", and directly set the initialized dictionary: 这在Python 2和Python 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}}]

What's going on here? 这里发生了什么? I have tried setting the variable "inside_init_dict" in various places, like as a class variable or outside the class. 我尝试在各种地方设置变量“ inside_init_dict”,例如作为类变量或在类外部。 The issue still occurs. 问题仍然存在。

In the first example you create a single dictionary inside_init_dict outside the loop and put it in multiple places. 在第一个示例中,您在循环外部创建了一个字典inside_init_dict ,并将其放在多个位置。 Every element of the list gets that same inside_init_dict . 列表中的每个元素都具有相同的inside_init_dict What you're seeing is not other objects in a list being affected, there's just one object being shown multiple times. 您所看到的并不是列表中的其他对象受到影响,只有一个对象被多次显示。

In the second example: 在第二个示例中:

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

Now each x gets its own dictionary. 现在,每个x都有自己的字典。 They all have the same value at first, but they are different instances, like identical twins. 一开始它们都具有相同的值,但是它们是不同的实例,就像同卵双胞胎一样。

This happens because dicts are mutable , meaning that you can change their content without changing their identity. 发生这种情况是因为dicts易变的 ,这意味着您可以更改其内容而无需更改其标识。 Here's a much simpler example of the behavior you're seeing: 这是您看到的行为的一个简单得多的示例:

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

Why this happens: 为什么会这样:

In the first line of this code, I create a new dictionary, {"key" : "value"} , and assign the name my_dict to it. 在此代码的第一行中,我创建了一个新字典{"key" : "value"} ,并为其分配名称my_dict

In the second line, I create a list, whose zeroth and first element both point to my_dict . 在第二行中,我创建一个列表,其第零个元素和第一个元素指向my_dict

In the third line, I access my_dict (through my_list[0] ), and I mutate it: changing the value associated with "key" . 在第三行中,我访问my_dict (通过my_list[0] ),并对它进行突变 :更改与"key"关联的值。

In the fourth line, I check the value of my_list . 在第四行中,我检查my_list的值。 Both the zeroth and the first element of my_list still point to my_dict -- and we've changed my_dict . my_list的第零个元素和第一个元素都仍指向my_dict并且我们更改了my_dict So the change is reflected in both elements of the list. 因此,更改将反映在列表的两个元素中。

One way to fix it: 解决它的一种方法:

Instead pointing to the same dictionary twice, create two dictionaries that have the same value: 而不是两次指向同一个字典,而是创建两个具有相同值的字典:

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

Assign the keys to different copies of the inside_init_dict dictionary instead of the same one: 将密钥分配给inside_init_dict词典的不同副本,而不是相同的副本:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM