名单列表改变整个子列表反映意外(List of lists changes reflected ac

2019-10-29 07:58发布

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

这不是我想要的东西或预期。 有人可以请解释一下这是怎么回事,以及如何解决它?

Answer 1:

当你写[x]*3 ,你得到的,基本上,列表[x, x, x] 也就是说,与3个引用同一个列表x 。 当你修改了这个单x是通过三个引用可见。

为了解决这个问题,你需要确保你在每个位置创建一个新的列表。 一种方式来做到这一点是

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

这将重新评估[1]*4每次代替一次评价它,使3个引用1名列表。


你可能会问,为什么*不能做出独立的对象列表理解的方式做。 这是因为乘法运算符*的对象进行操作,没有看到表情。 当使用*乘以[[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。



Answer 2:

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

住Python的导师可视化



Answer 3:

其实,这正是你所期待的。 让我们分解这里发生了什么:

你写

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 xrange(3) ]

在这种情况下,表达被重新评估对于每个n,从而导致不同的列表。



Answer 4:

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

它可能是一个有点不太令人吃惊。



Answer 5:

除了能够正确解释的问题,你的列表理解中,如果你正在使用python-2.x的使用接受的答案xrange()返回一个发生器,它是更有效的( range()在Python 3做相同的工作) _代替的不重磨变量的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])


Answer 6:

简单地说,这是因为发生在Python中一切正常引用 ,所以当你创建列表清单这样你基本结束了这样的问题。

1.使用numpy的数组:为了解决您的问题,你可以做任何他们中的一个为numpy.empty文档,当你到一个列表2.追加列表。 3.您还可以使用字典,如果你想



Answer 7:

Python的容器包含对其他对象的引用。 见下面的例子:

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

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

列表的一个整数乘法相当于多次(见添加列表以本身共同序列的操作 )。 因此,继续该示例:

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

我们可以看到,该列表c现在包含两个引用列出a这相当于c = b * 2

Python的FAQ也包含了对这种行为的解释: 如何创建一个多维列表?



Answer 8:

myList = [[1]*4] * 3创建一个列表对象[1,1,1,1]在存储器并复制其参考3倍以上。 这相当于obj = [1,1,1,1]; myList = [obj]*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,1,1,1]其中每个1是原子和不是引用1重复4次。 这意味着如果我们做obj[2]=42 ,那么obj将成为[1,1,42,1] [42,42,42,42]一些可以假设。



Answer 9:

让我们改写通过以下方式你的代码:

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

所以,我们现在走一步看一步。 你有x1 ,和一个单一的元素列表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]]出于同样的原因,作为第一步。



Answer 10:

我想大家解释发生了什么。 我建议解决这个问题的一种方式:

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


Answer 11:

试图更多描述性解释,

操作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]]


Answer 12:

从@spelchekr Python列表乘法:[[...]] * 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 ;
  • ma=[&li, &li, &li]li是可变的,所以当调用ma[0][0]=1 ,MA [0] [0]是同样以&li[0]所以所有的&li实例将改变它的第一个地址为&1


Answer 13:

通过使用内置的列表功能,你可以这样做

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


文章来源: List of lists changes reflected across sublists unexpectedly