当前位置:  开发笔记 > 编程语言 > 正文

列表更改意外地反映在子列表中

如何解决《列表更改意外地反映在子列表中》经验,为你挑选了5个好方法。

我需要在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]]  

这不是我想要或期望的.有人可以解释一下发生了什么,以及如何解决这个问题?



1> CAdaker..:

当你写作时,[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.


从技术上讲,它仍然是正确的.`[4]*3`基本上相当于`x = 4; [x,x,x]`.但是,这确实不会导致任何问题,因为`4`是不可变的.另外,你的另一个例子并非如此.`a = [x]*3; 即使`x`是可变的,a [0] = 5`也不会引起问题,因为你不修改`x`,只修改`a`.我不会将我的回答描述为误导或错误 - 如果你正在处理不可变对象,你只是*不能*自己在脚下射击.
我很惊讶没有人指出这一点,这里的答案是误导性的.`[x]*3`存储3个引用,如`[x,x,x]`仅在`x`可变时才正确.这不适用于例如`a = [4]*3`,其中'a [0] = 5`,`a = [5,4,4].
@Allanqunzi你错了.做`x = 1000; lst = [x]*2; lst [0]是lst [1]` - >`True`.Python无论如何都不区分可变对象和不可变对象.

2> 小智..:
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]

框架和对象

Live Python Tutor Visualize



3> PierreBdR..:

实际上,这正是您所期望的.让我们分解这里发生的事情:

你写

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重新计算表达式,从而得到不同的列表.



4> Blair Conrad..:
[[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]]

它可能不那么令人惊讶.


您可以使用"is"运算符来发现这一点.ls [0]是ls [1]返回True.

5> Kasramvd..:

除了正确解释问题的接受答案之外,在列表理解中,如果使用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.onesnp.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])

推荐阅读
mobiledu2402852357
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有