我有以下代码,为什么第一个没有更改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)
答案 0 :(得分:6)
这是一种有点不直观的变量行为。之所以发生这种情况,是因为在Python中,变量总是引用值。
在某些语言中,我们倾向于将变量视为我们放置值的“盒子”;但是,在Python中,它们是引用,并且更像是标记或值的“昵称”。因此,当您将1归因于item
时,您只更改变量引用,而不是它所指向的列表。
图形表示可以提供帮助。下图显示了alist = [[1,2], [3,4], [5,6]]
鉴于此,让我们看看当我们执行你的第一个循环时会发生什么。
当您执行for item in alist
时,您要求解释器从列表中获取每个值,每次一个,将其放在变量item
上并在其中执行一些操作。例如,在第一个操作中,我们有了这个新模式:
请注意,我们不会将子列表复制到item
;相反,我们通过item
指向。然后,我们执行item = 1
- 但这是什么意思?这意味着我们将item
指向值1
,而不是指向子列表:
请注意旧引用丢失(它是红色箭头),现在我们有了新的引用。但我们只是更改了一个指向列表的变量 - 我们没有改变列表本身。
然后,我们进入循环的第二次迭代,现在item
指向第二个子列表:
当我们再次执行item = 1
时,我们只需将变量指向其他值,而不更改列表:
现在,当我们执行第二个循环时会发生什么?
第二个循环作为第一个循环开始:我们让item
引用第一个子列表:
然而,第一个区别是我们打电话给item.append()
。 append()
是方法,因此它可以更改正在调用的对象的值。正如我们所说,我们正在向item
指向的对象发送消息以附加值10.在这种情况下,操作是而不是正在在变量item
中创建,但直接在它引用的对象中!所以这是调用item.append()
:
但是,我们不仅会在列表中附加值!我们还分配了item.append()
返回的值。这将切断item
对子列表的引用,但这里有一个问题:append()
返回None
。
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可以帮助您了解正在发生的事情,因为它以图形方式执行每个步骤。 Check your own code running there!
答案 1 :(得分:4)
在第一个示例中,item
绑定到列表alist
中的每个元素,然后item
反弹到整数1
。这不会更改列表的元素 - 它只是将名称item
重新绑定到int
对象1
。
在第二个示例中,列表元素(本身是列表)由append()
变异。 item
仍绑定到子列表,因此item.append()
会改变子列表。
答案 2 :(得分:2)
您是否忘记list.append()
未返回列表本身但实际修改了列表?
item = 1
按预期执行。对于for循环的其余部分,item现在是1
,而不是它最初的列表。它不会重新分配item
是什么,这不是for-loops的作用。
但是,在你的第二个循环中,你现在正在分配item = None
,因为append函数不会返回任何内容,只是它会将项目附加到列表中:
>>> L = [1, 2, 3]
>>> L.append(4)
>>> L
[1, 2, 3, 4]
因此,您的代码基本上是说“浏览主列表中的每个子列表并将10
附加到其中”。
答案 3 :(得分:2)
=
运算符不会对第二个代码进行任何更改,使用.append
会导致alist
中的更改。使用以下行作为第二个代码中的第三行。您将看到相同的结果:
item.append(10)
在第一个代码item
中,item=1
指向另一个对象,因此alist
不会更改。在第二个代码中,您可以通过调用alist
方法对append
进行更改。
答案 4 :(得分:2)
它是python中的指针变量。
第一个例子:
for item in alist:
item = 1
每个项目都指向alist中的每个_item,但突然你改变了项目值,而不是alist中_item的值,因此没有任何改变为alist
第二个例子:
for item in alist:
item = item.append(10)
每个项目都指向alist中的每个_item,然后将一些内容附加到共享_item的相同内存位置的项目,因为alist中_item的结果值也会更改,alist也会更改。
答案 5 :(得分:1)
引用the document:
for循环对目标列表中的变量进行赋值。 这会覆盖以前对这些变量的所有分配,包括 在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
中对下一项的引用所覆盖。
答案 6 :(得分:0)
写作时
for item in alist:
您实际上是在liast
变量的item
中创建了每个项目的副本,而您不获取对item
的引用。
但是,append
就地更改了列表并且没有返回值,这就是为什么您将值更改为None
(到期)的原因分配 - 如果你删除它,你将使附加工作正常)。