当列表被传递到函数中时会发生什么?

4
我将尝试理解当列表作为函数参数传递时的处理方式。所以我做了以下操作:
我初始化了一个列表:
ll = [1,2,3,4] 

并定义一个函数:

def Foo(ar):
    ar += [11]

我将列表传递给了该函数:

Foo(ll)

当我打印它时,我得到了:

print ll # [1, 2, 3, 4, 11] #case(1)

到目前为止一切都很好。

现在我修改了该功能,以便重置列表,使其仅有一个元素:

def Foo(ar):
    ar = [11]

我回想起了语法:

我回想起了语法:

Foo(ll)

当我重新打印它时,结果是相同的列表:
print ll # [1, 2, 3, 4] # case(2)

我原以为列表是按引用传递的,所以无论我们在函数内对列表做什么都会改变从主程序传递的原始函数。因此,在情况(2)中,我期望以下结果:

print ll # [11] # case(2) expected result

我在这里漏了什么吗?


ar = [11] 将名称分配给一个完全不同的对象。如果您想替换列表的整个内容,请尝试 ar[:] = [11] - jonrsharpe
3
我发现你选择的名称和变量(ll11)令人不安... - rodrigo
对于名称和值的选择,我很抱歉。 - rnels12
3个回答

7

ar += [11]不仅仅是一个赋值操作,它是一个方法调用(被调用的方法是:__iadd__)。当Python执行该行时,它会调用该方法,然后将ar赋值为结果。 list__iadd__方法修改了当前的list

ar = [11]是一个赋值操作,因此它只是改变了局部名称ar的值。但是函数内部的ar名称与ll变量的值之间没有任何关联。

您可能想要的是:

ar[:] = [11]

严格来说,ar += [11] 实际上仍然是一个赋值语句;它需要像 ar = (1, 2, 3); ar += (11,) 这样的东西才能工作。 - user2357112
@user2357112 你说得对,我过于简化了。已修正。 - Bakuriu
谢谢你的帮助。我现在明白了。 - rnels12
这真的是我需要的:ar[:] = [11] - rnels12

5
你需要区分对象对象名称之间的差异。Python中的列表是可变对象,因此只要有对它的引用,它们就可以随时更改。
名称只是对象的引用。记住,在Python中,技术上不存在变量,只有名称和对象,并且对象绑定到名称上。
所以,在这方面,让我们看看你的代码:
def Foo1(ar):
    ar += [11]

这个函数接受一个列表作为参数,将其绑定到本地名称ar,然后修改该列表,添加一个新值。
但是另一段代码:
def Foo2(ar):
    ar = [11]

这个函数接收一个列表,并将其绑定到名称ar,然后重新绑定名称ar为只有一个值的新列表。原始对象在该函数内部未被修改且被遗忘。
然后当您执行以下操作时:
a = [1,2,3]

然后调用:

Foo1(a)

修改列表以添加新值的方法,但是:
Foo2(a)

该语句不会改变a的值。

如果你想编写一个函数用于替换列表中的所有内容,可以使用以下方式:

def Foo3(a):
    a[:] = [11]

这段代码并没有重新绑定a,而是通过用新列表替换内容来修改列表。
为了进一步说明我的观点,请看下面的例子:
a = []
b = []
c = a
a.append(1)
b.append(2)
c.append(3)
print a
print b
print c

它将打印出:

[1, 3]
[2]
[1, 3]

你能告诉我为什么吗?


因为名称a和c现在都用来引用相同的对象吗? 顺便感谢解释。 - rnels12

2

增量赋值操作符如+=,执行的是普通赋值操作符=不能完成的功能。当你为一个变量赋值,像这样:

ar = [11]

这是对 ar 名称的操作。之前 ar 引用的列表对象没有改变,所以当您在函数外部执行 print ll 时,您看到的是原始列表。

当您执行

ar += [11]

这并不完全等同于

ar = ar + [11]

如果你执行ar = ar + [11]ll将不会改变。然而,+=会请求如果可能的话,ar所引用的列表应该通过修改自己的状态来执行操作,而不是创建一个新的列表。由于列表是可变的,列表会修改自身以具有一个新的11元素,并且这个变化可以通过ll变量看到。

感谢您的解释! - rnels12

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