我创建了一个列表的列表:
>>> 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:
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]]
除了正确解释问题的可接受答案之外,使用以下代码创建具有重复元素的列表:
[[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])
当你写[x]*3时,你会得到[x, x, x]这个列表。也就是说,一个有3个对同一个x的引用的列表。当你修改这个x时,它通过所有三个对它的引用都是可见的:
x = [1] * 4
xs = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(xs[0]): {id(xs[0])}\n"
f"id(xs[1]): {id(xs[1])}\n"
f"id(xs[2]): {id(xs[2])}"
)
# id(xs[0]): 140560897920048
# id(xs[1]): 140560897920048
# id(xs[2]): 140560897920048
x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"xs: {xs}")
# xs: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]
为了解决这个问题,您需要确保在每个位置都创建了一个新列表。一种方法是
[[1]*4 for _ in range(3)]
它将每次重新计算[1]*4,而不是计算一次并对1个列表进行3次引用。
You might wonder why * can't make independent objects the way the list comprehension does. That's because the multiplication operator * operates on objects, without seeing expressions. When you use * to multiply [[1] * 4] by 3, * only sees the 1-element list [[1] * 4] evaluates to, not the [[1] * 4 expression text. * has no idea how to make copies of that element, no idea how to reevaluate [[1] * 4], and no idea you even want copies, and in general, there might not even be a way to copy the element.
*的唯一选择是对现有子列表进行新的引用,而不是尝试创建新的子列表。其他任何东西都是不一致的,或者需要对基本语言设计决策进行重大的重新设计。
相比之下,列表推导式在每次迭代时重新计算元素表达式。[[1] * 4 for n in range(3)]每次重新计算[1]* 4,原因相同[x**2 for x in range(3)]每次重新计算x**2。每次对[1]* 4求值都会生成一个新列表,因此列表推导式执行您想要的操作。
顺便说一句,[1]* 4也不复制[1]的元素,但这没关系,因为整数是不可变的。你不能做类似于1的东西。Value = 2,把1变成2。