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

>>> 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:对于字典列表的类似问题,字典列表只存储每次迭代中最后追加的值

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


当前回答

虽然最初的问题使用乘法运算符构造子列表,但我将添加一个示例,该示例对子列表使用相同的列表。添加这个答案是为了完整性,因为这个问题经常被用作问题的规范

node_count = 4
colors = [0,1,2,3]
sol_dict = {node:colors for node in range(0,node_count)}

列表中的每个字典值都是同一个对象,试图改变其中一个字典值就会看到全部。

>>> sol_dict
{0: [0, 1, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}
>>> [v is colors for v in sol_dict.values()]
[True, True, True, True]
>>> sol_dict[0].remove(1)
>>> sol_dict
{0: [0, 2, 3], 1: [0, 2, 3], 2: [0, 2, 3], 3: [0, 2, 3]}

构造字典的正确方法是为每个值使用列表的副本。

>>> colors = [0,1,2,3]
>>> sol_dict = {node:colors[:] for node in range(0,node_count)}
>>> sol_dict
{0: [0, 1, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}
>>> sol_dict[0].remove(1)
>>> sol_dict
{0: [0, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}

其他回答

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

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

注意,序列中的项不会被复制;它们被多次引用。这经常困扰着新的Python程序员;考虑:

>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]

[[]]是一个包含空列表的单元素列表,因此[[]]* 3的所有三个元素都是对这个空列表的引用。修改列表中的任何元素都会修改这个列表。

另一个解释这一点的例子是使用多维数组。

你可能尝试过这样做一个多维数组:

>>> A = [[None] * 2] * 3

如果你打印出来,看起来是正确的:

>>> A
[[None, None], [None, None], [None, None]]

但当你赋值时,它会出现在多个地方:

>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]

原因是使用*复制列表不会创建副本,它只创建对现有对象的引用。3创建一个包含3个对长度为2的相同列表的引用的列表。对一行的更改将显示在所有行中,这几乎肯定不是您想要的。

@spelchekr from Python list乘法:[[…]*3制作了3个列表,这些列表在修改时相互镜像,我有同样的问题 “为什么只有外层的*3会产生更多的参考,而内部的则不会?”为什么不都是1s?”

li = [0] * 3
print([id(v) for v in li])  # [140724141863728, 140724141863728, 140724141863728]
li[0] = 1
print([id(v) for v in li])  # [140724141863760, 140724141863728, 140724141863728]
print(id(0))  # 140724141863728
print(id(1))  # 140724141863760
print(li)     # [1, 0, 0]

ma = [[0]*3] * 3  # mainly discuss inner & outer *3 here
print([id(li) for li in ma])  # [1987013355080, 1987013355080, 1987013355080]
ma[0][0] = 1
print([id(li) for li in ma])  # [1987013355080, 1987013355080, 1987013355080]
print(ma)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]

下面是我在尝试上面的代码后的解释:

内部的*3也创建引用,但它的引用是不可变的,就像[&0,&0,&0],那么当你改变li[0]时,你不能改变const int 0的任何底层引用,所以你可以只改变引用地址为新的&1; 而ma = [&li, &li, &li]且li是可变的,因此当你调用ma[0][0] = 1时,ma[0][0]等于&li[0],因此所有的&li实例将其第一个地址更改为&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])

通过使用内置的列表函数,您可以这样做

a
out:[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#Displaying the list

a.remove(a[0])
out:[[1, 1, 1, 1], [1, 1, 1, 1]]
# Removed the first element of the list in which you want altered number

a.append([5,1,1,1])
out:[[1, 1, 1, 1], [1, 1, 1, 1], [5, 1, 1, 1]]
# append the element in the list but the appended element as you can see is appended in last but you want that in starting

a.reverse()
out:[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#So at last reverse the whole list to get the desired list