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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么+运算符不会在.append()的情况下更改列表?相关的知识,希望对你有一定的参考价值。

我正在通过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并没有显示,但是当它不在函数中时它会显示?

答案

所以在一个函数中,在集合中添加6并没有显示,但是当它不在函数中时它会显示?

不,这不是发生的事情。

当你执行mylist = mylist + [6]时,你正在有效地创建一个全新的列表并将其放在本地mylist变量中。执行该函数后,此mylist变量将消失,新创建的列表也将消失。

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

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

What happens when you call proc()

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

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

然而,操作mylist + [6]创建了一个全新的列表对象,其内容是mylist指向的对象的内容加上以下列表对象的内容 - 即[6]。由于您将此新对象归因于mylist,我们的场景会稍微改变,而mylist不再指向list1指向的同一对象:

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

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

请注意,最后,list1指向的对象不会更改。

What happens when you call proc2()

当你打电话给proc2()时,一切都会改变。首先,它是一样的:你创建一个列表......

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

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

函数结束后,局部变量将消失,但由它和list1指向的原始对象将被更改:

由于它仍然由list1指出,原始列表不会被销毁。

编辑:如果你想看看你眼前发生的所有这些事情,只需去this radically amazing simulator

*如果您不知道什么是垃圾收集器......那么,您将在了解自己的问题后立即发现。

另一答案

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

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

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

另一答案

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

global mylist

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

但是在第二个函数proc2中,您通过附加而不是分配来修改列表,因此不需要global关键字,并且列表中的更改显示在其他位置。

另一答案

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

另一答案

在第三行,你做到了这一点

list1=list1+[6]

所以,当你在上面的行之后做了下面的话,

print (list1)

你在start和proc过程中创建的print1打印了list1,它添加了list1 + [6],它在函数proc中创建了一个新列表。如果要追加[6],则不会创建新列表,而是将列表项追加到现有列表中。

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

list1 = [1,2,3,4]

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

另一答案

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

mylist = mylist + [6]

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

mylist += [6]

虽然它看起来像第一个版本一样,但实际上与以下相同:

mylist.extend([6])

(注意,extend获取一个iterable的内容并逐个添加它们,而append会获取它给出的任何内容并将其作为单个项添加。请参阅append vs. extend以获得完整的解释。)

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

String StringBuilder StringBuffer 的区别? 什么情况下用“+”运算符进行字符串连接比调用 StringBuffer/StringBuilder对象的 append(代

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

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

Vue 组件 prop 更改不会触发重新渲染

如何在没有按键的情况下更改大写锁定状态

在这种情况下会有对象切片吗?