Python:迭代和修改嵌套列表

时间:2017-06-20 18:34:51

标签: python list

如果以前曾经问过这个问题我很抱歉,因为它似乎非常基本。不幸的是,当我搜索我的问题时,我只能找到其他问题,要求如何迭代列表列表,而这些问题都没有触及我所询问的特定行为。

我知道在python中,当你将两个列表等同时,你实际上并没有在内存中复制该列表,只是在内存中创建一个新的alais 指向到该列表。像

这样的东西
listA = [1,2,3]
listB = listA
listB[0] = 5
print(listA) #prints [5,2,3]

对我来说非常有意义。

我也知道你可以在for循环中修改可变类型(如列表),而对于其他类型(如整数),你不能,并且必须修改原始列表。例如

listA = [1,2,3]
listB = [4,5,6]
for Int in listA:
    Int +=1
print(listA) #doesn't work, prints [1,2,3]

for List in [listA,listB]:
    List[2] = 100
print(listA) #works, prints [1,2,100]
当我试图将这两个原则结合起来时,我的问题出现了。这是我尝试做的一个例子:

x = [1.2345,0.543895,0.0]
y = [2,3,4]
z = [65.34,3.248578493,1.11111]
for coord in [x,y,z]:
    rounded_coord = [round(item,3) for item in coord]
    coord = rounded_coord
print(x,y,z) #prints unmodified numbers

在这个例子中,' coord'是一个列表,因此我应该能够修改它,就像我之前的例子中的listA一样,但我不能。我必须使用枚举:

x = [1.2345,0.543895,0.0]
y = [2,3,4]
z = [65.34,3.248578493,1.11111]
coordlist = [x,y,z]
for idx,coord in enumerate(coordlist):
    coordlist[idx] = [round(item,3) for item in coord]
print(coordlist)

为什么我的原始尝试不起作用?

7 个答案:

答案 0 :(得分:0)

  

' COORD'是一个列表,因此我应该能够修改它......

几乎,但不完全。 coord是一个变量,每次迭代依次存储对每个原始列表的引用。

rounded_coord也是一个存储对新列表的引用的变量。

现在,执行coord = rounded_coord会使变量coord指向与rounded_coord相同的引用。这意味着coords的原始内容在coord指向更改的引用时将保持不变。

示例:

>>> x = [1, 2, 3, 4, 5]
>>> for l in [x]:
...    print(id(l))
...    new_l = [1]
...    l = new_l
...    print(id(l))
...    
4309421160
4309421592

顺便说一下,id打印一个10位数字,表示变量指向的引用。您可以看到在开始时与结尾时,l中存储的引用会发生变化。

答案 1 :(得分:0)

问题是赋值永远不会改变。它只是创建一个新的引用,或者重新分配引用(这就是你正在做的事情)。您必须使用mutator方法,例如:

In [1]: x = [1.2345,0.543895,0.0]
   ...: y = [2,3,4]
   ...: z = [65.34,3.248578493,1.11111]
   ...: for coord in [x,y,z]:
   ...:     coord[:] = [round(item,3) for item in coord]
   ...: print(x,y,z)
   ...:
[1.234, 0.544, 0.0] [2, 3, 4] [65.34, 3.249, 1.111]

因此,即使coord[:] = [round(item,r) for item in cord] 看起来像一样,也不是常规作业。调用mutator方法是语法糖,准确地说__setitem__

In [2]: coord
Out[2]: [65.34, 3.249, 1.111]

In [3]: coord.__setitem__(slice(None,None), ['foo','bar','baz'])

In [4]: coord
Out[4]: ['foo', 'bar', 'baz']

同样地,List[0] = 100 不是作业,它是语法糖:

In [6]: List
Out[6]: [1, 2, 3]

In [7]: List[0] = 99

In [8]: List
Out[8]: [99, 2, 3]

In [9]: List.__setitem__(0, 'foo')

In [10]: List
Out[10]: ['foo', 2, 3]

你不能修改不可变类型,因为根据定义,它们没有mutator方法,即使有看起来的操作像mutator方法一样:

x = 10
x += 10 # looks like a mutator method but actually assigns a new object to x

注意,str类似于列表。您可以对其进行切片,但相应的mutator方法不存在:

In [17]: s = 'abcdefg'

In [18]: s[:3]
Out[18]: 'abc'

In [19]: s.__getitem__(slice(0,3))
Out[19]: 'abc'

In [20]: s.__setitem__
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-20-44ee26769121> in <module>()
----> 1 s.__setitem__

AttributeError: 'str' object has no attribute '__setitem__'

答案 2 :(得分:0)

在你的例子中:

x = [1.2345,0.543895,0.0]
y = [2,3,4]
z = [65.34,3.248578493,1.11111]
for coord in [x,y,z]:
    rounded_coord = [round(item,3) for item in coord]
    coord = rounded_coord
print(x,y,z) #prints unmodified numbers

coord不是列表 - 它是指向ad-hoc创建的[x, y, z]列表中列表的指针。由于这是一个相对简单的例子,这里是如何在没有循环的情况下解压缩:

coord = x
rounded_coord = [round(item,3) for item in coord]
coord = rounded_coord

coord = y
rounded_coord = [round(item,3) for item in coord]
coord = rounded_coord

coord = z
rounded_coord = [round(item,3) for item in coord]
coord = rounded_coord

看到它的问题?

如果您真的想要更改元素本身,则必须在替换时通过其容器列表中的索引引用它(因为您已注意到它与enumerate一起使用)或替换值到位:

for coord in [x,y,z]:
    coord[:] = [round(item,3) for item in coord]  # assuming not overriden __setslice__()

答案 3 :(得分:0)

您的第一个示例无效,因为coord仅是对xyz列表的引用。当您重新分配coord时,您将覆盖原始引用并设置coord以引用新的列表对象。

换句话说,coord不是每个列表的指针。相反,它只是一个参考。当您重新分配coord时,您将失去原始参考。

您可以通过改变列表对象coord引用来解决此问题:

>>> x = [1.2345, 0.543895, 0.0]
>>> y = [2, 3, 4]
>>> z = [65.34, 3.248578493, 1.11111]
>>> 
>>> for coord in [x, y, z]:
    coord[:] = [round(item,3) for item in coord]


>>> x
[1.234, 0.544, 0.0]
>>> y
[2, 3, 4]
>>> z
[65.34, 3.249, 1.111]
>>>

答案 4 :(得分:0)

在所有情况下,循环变量只是被循环的东西中每个元素的别名。

您的第二个示例中coord的工作方式与您希望的相同,但第三个示例中的List并不是,在第二个示例中,您正在改变coord指向的列表元素,因此实际更新了基础数据。

在第三个示例中,您尝试直接更改coord,因此只是更新别名以指向其他列表,而不是使用别名来访问基础数组并更改它。因此,for List in [listA,listB]: List = [7,8,9] print(listA) # prints [1,2,3]; underlying data not changed print(List) # prints [7,8,9]; List now points to the new array 现在只指向不同的列表。考虑第二个示例的修改版本:

<form id="Form1" method="post" encType="multipart/form-data" runat="server">

答案 5 :(得分:0)

要修改列表的元素,您需要提供索引然后进行修改。换句话说,这不会修改列表中的任何内容:

arr=[1,2,3]
for x in range(len(arr)): #len(arr) evaluates to 3 and range(3) makes an array of numbers from 0 up to but not including 3
   arr[x]=arr[x]+1
print[arr] #Now prints [2,3,4]

所以即使你增加了值,但你没有替换列表中的值

您需要做的是,使用索引来更改值

for item in list:
    #do something

答案 6 :(得分:0)

每次使用表单循环时:

item[2] = 5

该项在循环时成为对列表中每个元素的引用。使用修饰符,例如:

item = some_other_thing

将起作用,因为您正在直接修改引用。

但是,当您尝试将引用设置为等于另一个变量时:

{{1}}

你实际上并没有修改项目,你正在修改对项目的引用,并且因此从未更改过原始项目,只是丢失了对它的引用。