10

我正在研究 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 添加到集合中不会显示,但在不在函数中时会显示?

4

6 回答 6

19

因此,在函数中将 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内容加上以下列表对象的内容 - 即[6]. 由于您将此新对象归因于mylist,因此我们的场景发生了一些变化,mylist不再指向 所指向的同一个对象list1

mylist 指向新的列表对象

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

我的列表不见了

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

GC收集列表

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

当你打电话时会发生什么proc2()

当你打电话时,一切都会改变proc2()。起初,它是同一件事:你创建一个列表......

创建新的列表实例和全局变量

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

调用方法创建局部变量

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

将值附加到列表

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

还是改了

由于它仍然由 指向list1,因此原始列表不会被破坏。

编辑:如果你想看看在你眼前发生的所有这些事情,那就去这个令人惊叹的模拟器

在此处输入图像描述

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

于 2012-05-25T03:40:29.383 回答
3

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

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

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

于 2012-05-25T03:41:03.000 回答
2

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

global mylist

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

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

于 2012-05-25T03:37:23.017 回答
2

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

mylist = mylist + [6]

...但仍希望“就地”更新列表,您可以执行以下操作:

mylist += [6]

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

mylist.extend([6])

(请注意,它extend获取一个可迭代对象的内容并一个接一个地添加它们,而append获取它给出的任何内容并将其添加为单个项目。请参阅附加与扩展以获得完整的解释。)

于 2012-06-07T11:35:13.930 回答
0

以一种或另一种形式,这是一个非常普遍的问题。几天前,我在解释传递给自己的 Python 参数时遇到了麻烦。基本上,其中一个创建一个新列表,另一个修改现有列表。在后一种情况下,所有引用列表的变量都“看到”了变化,因为它仍然是同一个对象。

于 2012-05-25T04:23:26.630 回答
0

在第三行,你这样做了

list1=list1+[6]

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

print (list1)

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

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

list1 = [1,2,3,4]

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

于 2012-05-25T08:00:53.470 回答