列表更改列表意外反映到子列表中

Python

米亚

2020-05-28

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

这不是我想要或期望的。有人可以解释发生了什么,以及如何解决吗?

第4216篇《列表更改列表意外反映到子列表中》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

11个回答
飞云 2020.05.28

myList = [[1]*4] * 3[1,1,1,1]在内存中创建一个列表对象,然后将其引用复制3次。这等效于obj = [1,1,1,1]; myList = [obj]*3列表中引用的obj任何地方,对的任何修改都会在三个地方反映出来obj正确的声明是:

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

要么

myList = [[1 for __ in range(4)] for _ in range(3)]

这里要注意的重要一点是,*运算符通常用于创建文字列表虽然1是一成不变的,obj =[1]*4但仍将创建1重复4遍以上的列表[1,1,1,1]但是,如果对不可变对象进行了任何引用,则该对象将被新对象覆盖。

这意味着,如果我们这样做obj[1]=42,那么obj它将变得[1,42,1,1] 不像 [42,42,42,42]某些人想象的那样。这也可以验证:

>>> myList = [1]*4
>>> myList
[1, 1, 1, 1]

>>> id(myList[0])
4522139440
>>> id(myList[1]) # Same as myList[0]
4522139440

>>> myList[1] = 42 # Since myList[1] is immutable, this operation overwrites myList[1] with a new object changing its id.
>>> myList
[1, 42, 1, 1]

>>> id(myList[0])
4522139440
>>> id(myList[1]) # id changed
4522140752
>>> id(myList[2]) # id still same as myList[0], still referring to value `1`.
4522139440
神乐 2020.05.28

通过使用内置列表功能,您可以像这样

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
杜大爷 2020.05.28

让我们以以下方式重写您的代码:

x = 1
y = [x]
z = y * 4

myList = [z] * 3

然后,运行以下代码使所有内容更加清晰。该代码所做的基本上是打印id获得的对象的,

返回对象的“身份”

并将帮助我们识别它们并分析发生的情况:

print("myList:")
for i, subList in enumerate(myList):
    print("\t[{}]: {}".format(i, id(subList)))
    for j, elem in enumerate(subList):
        print("\t\t[{}]: {}".format(j, id(elem)))

您将获得以下输出:

x: 1
y: [1]
z: [1, 1, 1, 1]
myList:
    [0]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [1]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [2]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528

现在让我们逐步进行。你有x哪些是1和一个单一的元素列表y包含x第一步是y * 4将获得一个z基本上是的新列表[x, x, x, x]即创建一个包含4个元素的新列表,这些元素是对初始x对象的引用净步骤非常相似。基本上z * 3您要做的[[x, x, x, x]] * 3和返回[[x, x, x, x], [x, x, x, x], [x, x, x, x]],其原因与第一步相同。

DavaidTony宝儿 2020.05.28

@spelchekr来自Python的列表乘法:[[...]] * 3使得3个列表在修改后会相互镜像,我也有一个相同的问题:“为什么只有外部* 3创建更多引用,而内部* 3却没有创建更多引用为什么不是全1?”

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
  • while ma=[&li, &li, &li]limutable是可变的,因此当您调用时ma[0][0]=1,ma [0] [0]等于to &li[0],因此所有&li实例都将其第一个地址更改为&1
2020.05.28

我想每个人都解释发生了什么。我建议一种解决方法:

myList = [[1 for i in range(4)] for j in range(3)]

myList[0][0] = 5

print myList

然后您有:

[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
小胖Gil 2020.05.28

Python容器包含对其他对象的引用。请参阅以下示例:

>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]

在此b列表中包含一个项目,该项目是对list的引用a该列表a是可变的。

将列表乘以整数等于将列表多次添加到自身(请参阅常见序列操作)。因此,继续下面的示例:

>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]

我们可以看到列表c现在包含两个对list的引用,a它们等效于c = b * 2

Python FAQ也包含此行为的解释:如何创建多维列表?

千羽 2020.05.28

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

你写

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

猴子村村 2020.05.28

简而言之,这是因为在python中,所有内容都可以通过引用来工作,因此,当您以这种方式创建列表列表时,基本上会遇到此类问题。

要解决您的问题,您可以执行以下任一操作:1.将numpy数组文档用于numpy.empty。2 .将列表追加到列表中。3.您也可以使用字典

达蒙 2020.05.28

除了可以正确解释问题的公认答案之外,在列表理解范围内,如果您使用的是python-2.x,则使用return会xrange()生成一个更有效的生成器(range()在python 3中执行相同的工作),_而不是throwaway变量n

[[1]*4 for _ in xrange(3)]      # and in python3 [[1]*4 for _ in range(3)]

另外,作为一种Python方式,您可以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])
DavaidTony宝儿 2020.05.28
[[1] * 4] * 3

甚至:

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

创建一个引用内部[1,1,1,1]3次的列表-而不是内部列表的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]]

可能不那么令人惊讶。

GO 2020.05.28
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]

框架和物体

实时Python导师可视化

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android