我需要在Python中创建一个列表列表,所以我输入以下内容:
myList = [[1] * 4] * 3
列表看起来像这样:
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
然后我改变了最里面的一个值:
myList[0][0] = 5
现在我的列表看起来像这样:
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
这不是我想要或期望的.有人可以解释一下发生了什么,以及如何解决这个问题?
当你写作时,[x]*3
你基本上得到了这个列表[x, x, x]
.也就是说,列表中有3个引用相同的列表x
.然后,当您修改此单个时x
,通过对它的所有三个引用都可以看到它.
要解决此问题,您需要确保在每个位置创建新列表.一种方法是
[[1]*4 for _ in range(3)]
这将[1]*4
每次重新评估,而不是评估一次,并对3个列表进行3次引用.
您可能想知道为什么*
不能像列表理解那样创建独立对象.那是因为乘法运算符*
对对象进行操作,而不会看到表达式.当您使用*
乘以[[1] * 4]
3时,*
只看到1元素列表的[[1] * 4]
计算结果,而不是[[1] * 4
表达式文本.*
不知道如何制作该元素的副本,不知道如何重新评估[[1] * 4]
,也不知道你甚至想要副本,一般来说,甚至可能没有办法复制元素.
唯一的选择*
是对现有子列表进行新的引用,而不是尝试创建新的子列表.其他任何事情都会不一致或需要重新设计基础语言设计决策.
相反,列表推导会重新评估每次迭代时的元素表达式.每次[[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.
size = 3 matrix_surprise = [[0] * size] * size matrix = [[0]*size for i in range(size)]
Live Python Tutor Visualize
实际上,这正是您所期望的.让我们分解这里发生的事情:
你写
lst = [[1] * 4] * 3
这相当于:
lst1 = [1]*4 lst = [lst1]*3
这意味着lst
是一个包含3个元素的列表lst1
.这意味着以下两行是等效的:
lst[0][0] = 5 lst1[0] = 5
就像lst[0]
什么一样lst1
.
要获得所需的行为,您可以使用列表理解:
lst = [ [1]*4 for n in range(3) ] #python 3 lst = [ [1]*4 for n in xrange(3) ] #python 2
在这种情况下,对每个n重新计算表达式,从而得到不同的列表.
[[1] * 4] * 3
甚至:
[[1, 1, 1, 1]] * 3
创建一个引用内部[1,1,1,1]
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-2.x xrange()
,则返回一个更有效的生成器(range()
在python 3中执行相同的工作)_
而不是一次性变量n
:
[[1]*4 for _ in xrange(3)] # and in python3 [[1]*4 for _ in range(3)]
此外,作为更多Pythonic方式,您可以使用它itertools.repeat()
来创建重复元素的迭代器对象:
>>> a=list(repeat(1,4)) [1, 1, 1, 1] >>> a[0]=5 >>> a [5, 1, 1, 1]
PS使用numpy的,如果你只是想创建1或0,你可以使用数组np.ones
和np.zeros
和/或其他使用次数np.repeat()
:
In [1]: import numpy as np In [2]: In [2]: np.ones(4) Out[2]: array([ 1., 1., 1., 1.]) In [3]: np.ones((4, 2)) Out[3]: array([[ 1., 1.], [ 1., 1.], [ 1., 1.], [ 1., 1.]]) In [4]: np.zeros((4, 2)) Out[4]: array([[ 0., 0.], [ 0., 0.], [ 0., 0.], [ 0., 0.]]) In [5]: np.repeat([7], 10) Out[5]: array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])