我创建了一个列表的列表:

>>> xs = [[1] * 4] * 3
>>> print(xs)
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]

然后,我改变了最里面的一个值:

>>> xs[0][0] = 5
>>> print(xs)
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

为什么每个子列表的第一个元素都变成了5?


参见:

我如何克隆一个列表,使它不会在分配后意外改变?寻找解决问题的方法

Python:对于字典列表的类似问题,字典列表只存储每次迭代中最后追加的值

如何初始化一个字典,其值是不同的空列表?对于列表字典的类似问题


当前回答

[[1] * 4] * 3

甚至:

[[1, 1, 1, 1]] * 3

创建一个3次引用内部[1,1,1,1]的列表——而不是内部列表的3个副本,因此任何时候修改列表(在任何位置),您都会看到3次更改。

和下面这个例子一样:

>>> inner = [1,1,1,1]
>>> outer = [inner]*3
>>> outer
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> inner[0] = 5
>>> outer
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

在那里可能不那么令人惊讶。

其他回答

除了正确解释问题的可接受答案之外,使用以下代码创建具有重复元素的列表:

[[1]*4 for _ in range(3)]

同样,你可以使用itertools.repeat()创建一个重复元素的迭代器对象:

>>> a = list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0] = 5
>>> a
[5, 1, 1, 1]

注:如果你使用NumPy,你只想创建一个数组的1或0,你可以使用np。1和np。0和/或其他数字使用np.repeat:

>>> import numpy as np
>>> np.ones(4)
array([1., 1., 1., 1.])
>>> np.ones((4, 2))
array([[1., 1.],
       [1., 1.],
       [1., 1.],
       [1., 1.]])
>>> np.zeros((4, 2))
array([[0., 0.],
       [0., 0.],
       [0., 0.],
       [0., 0.]])
>>> np.repeat([7], 10)
array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for _ in range(size)]

使用Python导师进行实时可视化:

为了更详细地解释它,

操作1:

x = [[0, 0], [0, 0]]
print(type(x)) # <class 'list'>
print(x) # [[0, 0], [0, 0]]

x[0][0] = 1
print(x) # [[1, 0], [0, 0]]

操作2:

y = [[0] * 2] * 2
print(type(y)) # <class 'list'>
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [1, 0]]

注意到为什么修改第一个列表的第一个元素不修改每个列表的第二个元素吗?这是因为[0]* 2实际上是两个数字的列表,对0的引用不能被修改。

如果您想创建克隆副本,请尝试操作3:

import copy
y = [0] * 2   
print(y)   # [0, 0]

y = [y, copy.deepcopy(y)]  
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [0, 0]]

另一种创建克隆副本的有趣方法,操作4:

import copy
y = [0] * 2
print(y) # [0, 0]

y = [copy.deepcopy(y) for num in range(1,5)]
print(y) # [[0, 0], [0, 0], [0, 0], [0, 0]]

y[0][0] = 5
print(y) # [[5, 0], [0, 0], [0, 0], [0, 0]]

简单地说,这是因为在python中,一切都是通过引用工作的,所以当你以这种方式创建一个列表的列表时,你基本上会遇到这样的问题。

为了解决你的问题,你可以做其中之一: 1. 使用numpy.empty的numpy数组文档 2. 当您到达一个列表时,请添加该列表。 3.如果你愿意,你也可以用字典

我正在补充我的答案,以图解方式解释同样的问题。

你创建2D的方式,创建一个浅列表

arr = [[0]*cols]*row

相反,如果您希望更新列表中的元素,则应该使用

rows, cols = (5, 5) 
arr = [[0 for i in range(cols)] for j in range(rows)] 

解释:

可以使用以下命令创建列表:

arr = [0]*N 

or

arr = [0 for i in range(N)] 

在第一种情况下,数组的所有下标都指向同一个整数对象

当你给一个特定的索引赋值时,就会创建一个新的int对象,例如arr[4] = 5

现在让我们看看当我们创建一个list of list时会发生什么,在这种情况下,top list的所有元素都指向同一个列表

如果你更新任何索引的值,就会创建一个新的int对象。但是由于所有顶级列表索引都指向同一个列表,所以所有行看起来都是一样的。您会觉得更新一个元素就是更新该列中的所有元素。

感谢Pranav Devarakonda提供的简单解释