使用列表乘法创建的嵌套引用列表的深拷贝无效

8 投票
2 回答
2583 浏览
提问于 2025-04-16 11:01

虽然我很喜欢Python,但有时候关于引用和深拷贝的东西让我感到困惑。

为什么在这里深拷贝不起作用:

>>> import copy
>>> a = 2*[2*[0]]
>>> a
[[0, 0], [0, 0]]
>>> b = copy.deepcopy(a)
>>> b[0][0] = 1
>>> b
[[1, 0], [1, 0]]     #should be: [[1, 0], [0, 1]]
>>> 

我现在使用numpy数组作为解决办法,反正我后面也需要用到这个。不过我真的希望如果我使用深拷贝,就不需要再担心意外的引用问题了。还有其他地方深拷贝也不管用吗?

2 个回答

7

它的工作方式正如你所预期的那样。

a = 2*[2*[0]]

当你用 2 * 来乘以 [[0,0]] 时,新列表中的两个元素都会指向同一个 [0,0] 列表。也就是说,a[0]a[1] 是同一个列表,因为它们只是引用了同一个对象,而不是复制了数据(这其实是不可能的)。如果你改变其中一个的第一个元素,另一个的第一个元素也会跟着改变。

copy.deepcopy 可以正确地复制列表,确保每个对象都是独一无二的。

17

之所以不行,是因为你创建了一个数组,其中有两个指向同一个数组的引用。

一种替代的方法是:

[[0]*2 for i in range(2)]

或者更明确一点的方式是:

[[0 for j in range(2)] for i in range(2)]

这样做是有效的,因为它在每次循环时都会创建一个新的数组。

还有其他情况会导致不工作吗?

每当你有一个数组里面包含引用的时候,就要小心了。例如,[Foo()] * 2[Foo() for i in range(2)] 是不一样的。在第一个例子中,只创建了一个对象,数组里有两个指向它的引用。而在第二个例子中,创建了两个独立的对象。

撰写回答