为啥 + 运算符不更改列表而 .append() 呢?

Posted

技术标签:

【中文标题】为啥 + 运算符不更改列表而 .append() 呢?【英文标题】:Why does not the + operator change a list while .append() does?为什么 + 运算符不更改列表而 .append() 呢? 【发布时间】:2012-05-31 16:00:14 【问题描述】:

我正在研究 Udacity,Dave Evans 介绍了一个关于列表属性的练习

list1 = [1,2,3,4]
list2 = [1,2,3,4]

list1=list1+[6]
print(list1)
list2.append(6)
print(list2)

list1 = [1,2,3,4]
list2 = [1,2,3,4]

def proc(mylist):
    mylist = mylist + [6]

def proc2(mylist):
    mylist.append(6)

# Can you explain the results given by the four print statements below? Remove
# the hashes # and run the code to check.

print (list1)
proc(list1)
print (list1)

print (list2)
proc2(list2)
print (list2)

输出是

[1, 2, 3, 4, 6]
[1, 2, 3, 4, 6]
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4, 6]

所以在函数中,将 6 添加到集合中不会显示,但在不在函数中时会显示?

【问题讨论】:

【参考方案1】:

通常,在函数proc 的第一种情况下,如果您声明,您只能通过赋值来更改全局列表

global mylist

首先并没有将mylist 作为参数传递。但是,在这种情况下,您会收到一条错误消息,指出 mylist 是全局和本地的: name 'mylist' is local and globalproc 中发生的事情是在分配发生时创建一个本地列表。由于局部变量会在函数结束时消失,因此对局部列表的任何更改的影响在随后打印出来时不会传播到程序的其余部分。

但在第二个函数proc2 中,您修改列表是通过附加而不是分配给它,所以global 关键字不是必需的,对列表的更改会显示在其他地方。 p>

【讨论】:

【参考方案2】:

所以在函数中将 6 添加到集合中不会显示,但在不在函数中时会显示?

不,事实并非如此。

发生的情况是,当您执行 mylist = mylist + [6] 时,您实际上是在创建一个全新的列表并将其放入本地 mylist 变量中。这个mylist变量会在函数执行后消失,新创建的列表也会消失。

OTOH 执行mylist.append(6) 时不会创建新列表。您已经在 mylist 变量中获得了该列表,并将一个新元素添加到同一个列表中。结果是列表(也由list2 指向)将自行更改。 mylist 变量将再次消失,但在这种情况下您更改了原始列表。

让我们看看更直观的解释是否可以帮助您:)

当您致电proc() 时会发生什么

当您编写list1 = [1, 2, 3, 4, 5] 时,您正在创建一个新的列表对象(在等号的右侧)并创建一个新变量list1,它将指向该对象。

然后,当您调用proc() 时,您会创建另一个新变量mylist,并且由于您将list1 作为参数传递,mylist 将指向同一个对象:

但是,操作mylist + [6] 创建了一个全新的列表对象,其内容是mylist指向的对象的内容加上后面的列表对象的内容——即@987654351 @。由于您将此新对象归因于mylist,因此我们的场景发生了一些变化,mylist 不再指向list1 指向的同一对象:

我没有说的是mylist是一个局部变量:它会在proc()函数结束后消失。所以,当proc() 执行结束时,mylist 就消失了:

由于没有其他变量指向mylist + [6]生成的对象,它也会消失(因为垃圾收集器*会收集它):

注意,最后list1指向的对象并没有改变。

当您拨打proc2() 时会发生什么

当您致电proc2() 时,一切都会发生变化。一开始是一样的:你创建一个列表...

...并将其作为参数传递给函数,该函数将生成一个局部变量:

但是,您可以将append() 方法应用于现有列表,而不是使用生成新列表的+ 连接运算符。 append() 方法不创建新对象;相反,它_changes现有的:

函数结束后,局部变量会消失,但它和list1指向的原始对象已经改变了:

由于它仍然被list1指向,所以原始列表没有被破坏。

编辑:如果您想看看眼前发生的所有事情,请转至this radically amazing simulator:

* 如果你不知道什么是垃圾收集器......好吧,在理解你自己的问题后你很快就会发现。

【讨论】:

也有好的service 一步一步去python代码看浏览器中的所有变量!非常接近你写的,@brandizzi @stalk 我不知道这项服务。太好了!谢谢! 另外,***.com/questions/986006/… 也有更好的解释! 你的答案比你在评论中链接的更接近the truth。 请注意,垃圾收集器不是负责释放该列表的人。相反,列表的引用计数达到 0,因此立即释放内存。 python 的垃圾收集器只是为了打破引用循环。【参考方案3】:

python 中的变量总是可以被认为是引用。当您使用参数调用函数时,您传递的是对实际数据的引用。

当您使用赋值运算符 (=) 时,您将分配该名称以引用一个全新的对象。因此,mylist = mylist + [6] 创建了一个新列表,其中包含 mylist 的旧内容以及 6,并分配变量 mylist 以引用新列表。 list1 仍然指向旧列表,所以没有任何变化。

另一方面,当您使用 .append 时,这实际上是将一个元素附加到变量所引用的列表中 - 它没有为变量分配任何新内容。所以你的第二个函数修改了 list2 引用的列表。

【讨论】:

【参考方案4】:

无论以何种形式,这是一个非常常见的问题。我在解释传递给我自己a couple days ago 的 Python 参数时大吃一惊。基本上,其中一个创建一个新列表,另一个修改现有列表。在后一种情况下,所有引用列表的变量都会“看到”变化,因为它仍然是同一个对象。

【讨论】:

【参考方案5】:

在第三行,你这样做了

list1=list1+[6]

所以,当您在上述行之后执行以下操作时,

print (list1)

它打印了您在开始时创建的 list1 和添加 list1 + [6] 的 proc 过程,它正在函数 proc 中创建一个新列表。当您追加 [6] 时,您不是在创建新列表,而是将列表项追加到现有列表中。

但是,请记住。在第 7 行,您再次创建了列表

list1 = [1,2,3,4]

现在您想通过显式调用来打印 list1,这将打印出您再次重新初始化的 list1,但不会打印前一个。

【讨论】:

【参考方案6】:

除了已经给出的全面答案之外,还值得注意的是,如果您想要与以下相同的语法:

mylist = mylist + [6]

...但仍希望“就地”更新列表,您可以这样做:

mylist += [6]

虽然它看起来和第一个版本做同样的事情,但实际上与:

mylist.extend([6])

(请注意,extend 获取一个可迭代对象的内容并将它们一个接一个地添加,而 append 获取它给出的任何内容并将其添加为单个项目。有关完整说明,请参阅 append vs. extend。)

【讨论】:

以上是关于为啥 + 运算符不更改列表而 .append() 呢?的主要内容,如果未能解决你的问题,请参考以下文章

Python append() 与列表上的 + 运算符,为啥这些会给出不同的结果?

Python,明明只append了一次,为啥所有子列表都变了啊.jpg

为什么+运算符不会在.append()的情况下更改列表?

为啥 .append() 方法不适用于字符串,它们的行为不像列表?

为啥 Bindinglist 在数据库更改后不更新?

为啥你可以在函数中使用另一种方法而不是赋值运算符来更改 Ruby 中局部变量的值?