使用列表乘法创建的嵌套引用列表的深拷贝无效
虽然我很喜欢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)]
是不一样的。在第一个例子中,只创建了一个对象,数组里有两个指向它的引用。而在第二个例子中,创建了两个独立的对象。