为什么更改一个 Python 列表元素会更改所有其他元素?

Why does changing one Python list element change all the others?

提问人:SquiggleCat 提问时间:9/29/2023 最后编辑:SquiggleCat 更新时间:9/30/2023 访问量:65

问:

我有一些代码,我制作了一个字典,每个值都是列表。我发现递增任何列表索引都会递增每个值的相应索引;即 将递增彼此列表的 0 索引。[0,0]d[key][0] += 1

我认为 Python 对列表的浅层复制是罪魁祸首,因为我的字典是使用 .但是,我在列表中使用时遇到了同样的问题。 然后我尝试了字典理解:.这正如我所期望的那样工作;修改一个列表不会影响其他列表。dict.fromkeys(keys, [0,0])copy.deepcopy{key:[0,0] for key in keys}

既然我已经找到了解决这个问题的方法,我的实际问题是为什么会这样。似乎要么我对工作原理有误解,要么与手头的问题无关,但我不知道还有什么会导致这种情况。deepcopy

这是一个完整的代码片段,重现了以下行为:

import copy
keys = [i for i in range(5)]
l = [0,0]
d = dict.fromkeys(keys, copy.deepcopy(l))
#d = {key:[0,0] for key in keys} # this approach works as expected
d[0][0] += 1
print(d)
# expected: {0: [1, 0], 1: [0, 0], 2: [0, 0], 3: [0, 0], 4: [0, 0]}
# actual: {0: [1, 0], 1: [1, 0], 2: [1, 0], 3: [1, 0], 4: [1, 0]}
Python 字典 深拷贝

评论

0赞 Brian61354270 9/29/2023
提示:当你写作时,被评估了多少次?f(a, b)b
1赞 Sauron 9/30/2023
当您使用 时,甚至您正在为字典中每个键分配相同的列表对象作为值。这意味着修改一个列表将影响所有其他列表,因为它们本质上是对同一对象的引用。dict.fromkeys(keys, [0,0])copy.deepcopy(l)
0赞 Brian61354270 9/30/2023
旁注:实际上并没有制作浅层副本(如果这样做,您的问题将得到解决)。相反,它多次使用对完全相同列表的引用。此外,您可能有兴趣知道,对于整数列表,浅拷贝和深拷贝之间实际上没有区别(请参阅整数的不可变性dict.fromkeys(keys, [0,0])[0, 0])

答:

1赞 Barmar 9/29/2023 #1

您只制作列表的一个副本,并将其用于字典中的每个值。函数参数仅计算一次,结果将传递给函数。所以当你写

d = dict.fromkeys(keys, copy.deepcopy(l))

它实际上等同于

temp = copy.deepcopy(l)
d = dict.fromkeys(keys, temp)

请改用字典推导式,以便为每个元素创建一个新副本。

d = {key: copy.deepcopy(l) for key in keys}
2赞 Luke B 9/30/2023 #2

在这种情况下,对问题没有帮助,而是功能。 来自 https://docs.python.org/3/library/stdtypes.html#dict.fromkeys 的文档deepcopydict.fromkeys

所有值都仅引用单个实例,因此值通常是可变对象(如空列表)是没有意义的。若要获取非重复值,请改用字典推导式。

您将为值参数传递单个列表。因此,字典中的每个键都引用了一个列表,该列表恰好是从 中深度复制的。fromkeysl

因此,您的词典理解似乎是推荐的方法。由于您正在为每个元素创建一个新列表,因此 ,它们保持独立。[0, 0]

0赞 alec_djinn 9/30/2023 #3

我会以不同的方式制作字典。这里真的没有必要。copy.deepcopy

keys = list(range(5))
values = [[0,0] for _ in keys]
d = dict(zip(keys, values))

#one-liner: d = {k:[0,0] for k in range(5)}

d[0][0] += 1
print(d) #{0: [1, 0], 1: [0, 0], 2: [0, 0], 3: [0, 0], 4: [0, 0]}