Python:循环列表中的列表并在循环内部分配值

7

我有以下代码,为什么第一个不改变alist而第二个可以改变它?

alist = [[1,2], [3,4], [5,6]]
for item in alist:
    item = 1
print(alist)

alist = [[1,2], [3,4], [5,6]]
for item in alist:
    item = item.append(10)
print(alist)

如果像这样使用 for item in alist : alist[alist.index(item)]= 1 进行更改,那么 item 是循环中的局部变量,在外部不起作用。 - dsgdfg
7个回答

6
这是变量的一种有些不直观的行为方式。这是因为在Python中,变量总是指向值的引用。
盒子和标签
在某些语言中,我们倾向于将变量视为放置值的“盒子”;然而,在Python中,它们是引用,并且更像是对值的标签或“昵称”。因此,当你将1赋给item时,你只是改变了变量的引用,而不是它所指向的列表。
图形表示可以帮助理解。下面的图像表示由alist = [[1,2], [3,4], [5,6]]创建的列表。

A list with three sublists

考虑到这一点,让我们看看当执行你的第一个循环时会发生什么。

第一个循环

当你执行for item in alist时,你要求解释器每次从列表中取出一个值,将其放入变量item中并对其进行一些操作。例如,在第一个操作中,我们有了这个新模式:

Now a variable points to a sublist

请注意,我们不会复制子列表到item中;相反,我们通过item指向它。然后,我们执行item = 1——但这意味着什么?这意味着我们将item指向值1,而不是指向子列表:

item points to other value now

请注意,旧引用已丢失(即红色箭头),现在我们有了一个新引用。但我们只是改变了指向列表的变量 - 我们没有改变列表本身。
然后,我们进入循环的第二次迭代,现在item指向第二个子列表。

item pointing to the second sublist

当我们执行item = 1时,我们只是让变量指向另一个值,而不改变列表本身。

enter image description here

现在,当我们执行第二个循环时会发生什么?

第二个循环

第二个循环与第一个循环相同:我们让item引用第一个子列表:

Now a variable points to a sublist

然而,第一个区别在于我们调用了item.append()append()是一个方法,因此它可以更改调用它的对象的值。就像我们所说的那样,我们向item指向的对象发送一个消息以附加值10。在这种情况下,操作不是在变量item中进行的,而是直接在它所指的对象中进行的!因此,调用item.append()的结果如下:

A list has grown

然而,我们不仅向列表附加一个值!我们还将赋值给item.append()返回的值。这将切断item与子列表之间的引用,但这里有一个问题:append()返回None

enter image description here

None

None是一个表示基本上没有相关值可用的值。当函数返回None时,它通常是在说:"我没有任何相关的东西可以给你。"append()直接改变其列表,因此它不需要返回任何内容。

这很重要,因为你可能认为item会指向附加的列表[1, 2, 10],对吗?不,现在它指向None。所以,你会期望下面的代码...

alist = [[1,2], [3,4], [5,6]]
for item in alist:
    item = item.append(10)
    print(item)

要打印类似这样的内容:
[1, 2, 10]
[3, 4, 10]
[5, 6, 10]

但是这并不会发生。这才是发生的事情:
>>> alist = [[1,2], [3,4], [5,6]]
>>> for item in alist:
...     item = item.append(10)
...     print(item)
...
None
None
None

然而,正如我们所评论的那样,append()方法改变了列表本身。因此,在赋值之后,item变量是无用的,但最终的列表被修改了!

>>> print alist
[[1, 2, 10], [3, 4, 10], [5, 6, 10]]

如果您想在循环内使用附加的列表,只需不将方法返回值分配给item。做法如下:
>>> alist = [[1,2], [3,4], [5,6]]
>>> for item in alist:
...     item.append(10)
...     print item
... 
[1, 2, 10]
[3, 4, 10]
[5, 6, 10]

这可以工作,因为item仍将指向列表。

结论

引用起初有些复杂,更不用说掌握了。然而,它们非常强大,如果您遵循示例等方式进行学习,就可以学会使用它们。您的示例有些复杂,因为这里发生了更多的事情。

Python Tutor可以帮助您理解正在发生的事情,因为它会以图形方式执行每个步骤。在那里检查您自己的代码运行!


4
在第一个例子中,item绑定到列表alist中的每个元素上,然后item被重新绑定为整数1。这不会改变列表的元素 - 它只是将名称item重新绑定到int对象1
在第二个例子中,列表元素(它本身是一个列表)通过append()进行了变异。 item仍然绑定到子列表上,因此item.append()会改变子列表。

2
你忘记了list.append()并不返回列表本身,而是直接在原地修改列表。对于item = 1,它的作用如预期一样。在for循环的其余部分中,item现在是1,而不是它最初的列表。它不会重新分配item的值,因为这不是for循环的作用。然而,在你的第二个循环中,你现在正在赋值item = None,因为append函数不返回任何值,但它会将项目附加到列表中。
>>> L = [1, 2, 3]
>>> L.append(4)
>>> L
[1, 2, 3, 4]

因此,您的代码基本上是在说“遍历我的主列表中的每个子列表并将10添加到其中”。

2
< p > = 运算符不会对第二段代码进行任何更改,使用 .append 会导致 alist 发生变化。请在第二段代码的第三行中使用以下代码,您将看到相同的结果:

item.append(10)

在第一段代码中,item通过item=1指向另一个对象,因此alist没有改变。在第二段代码中,您通过调用alistappend方法对其进行了更改。

2

这是Python中的指针变量。

第一个例子:

for item in alist:
   item = 1

每个项目都指向alist中的每个_item,但突然您更改了项目值,而不是alist中的_item值,因此alist没有任何变化。
第二个例子:
for item in alist:
    item = item.append(10)

每个项目都指向alist中的_each_item_,然后您将某些内容附加到与 _item 共享内存位置的项目中,结果alist中的 _item 的值会更改,alist也会更改。


1
引用该文档

The for-loop makes assignments to the variables(s) in the target list. This overwrites all previous assignments to those variables including those made in the suite of the for-loop:

for i in range(10):
    print(i)
    i = 5             # this will not affect the for-loop
                      # because i will be overwritten with the next
                      # index in the range
所以你的第二个例子与以下内容完全相同:

alist = [[1,2], [3,4], [5,6]]
for item in alist:
    item.append(10)    # this statement modifies each item in alist
                       # , which is what item the variable "points to"
print(alist)

也就是说,item 是每次迭代中的一个新变量。因此,在任何特定的迭代中为其赋值都是没有意义的,因为在下一次迭代的开始时,它的值将被下一个在 alist 中的项所覆盖。请注意保留html标签。

0

当你写代码时

for item in alist:

你实际上是在 liast 中创建每个项目的副本并将其存储在 item 变量中,并且你没有得到对 item 的引用。
然而,append 在原地更改列表并不返回任何值,这就是为什么你会发现值被更改为 None(由于赋值 - 如果你删除它,你将得到正常的追加)。

在Python中的for each块不会进行任何复制,即在每次迭代中,item指向alist的每个成员。 - SuB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接