为什么更改对象类型会影响Python在传递引用时的行为?

时间:2017-08-02 06:06:55

标签: python pass-by-reference

我原本期望以下代码:

def a(l):
    l.append(3)

def b(l):
    l = 5

def c(l):
    l = (2,-2)

numbers = [1,2,3,4,5]
a(numbers)
print(numbers)
b(numbers)
print(numbers)
c(numbers)
print(numbers)

打印:

[1, 2, 3, 4, 5, 3]
5
(2,-2)

但它打印出来:

[1, 2, 3, 4, 5, 3]
[1, 2, 3, 4, 5, 3]
[1, 2, 3, 4, 5, 3]

为什么?

编辑:

这是因为通过引用传递通过共享调用之间的区别!

3 个答案:

答案 0 :(得分:3)

我认为这是因为Python(如C和Java)有效地传递值。对于a()函数,您按值传递引用。那里没有通过引用传递

另一方面,

传递传递别名,它具有与通过引用传递类似的行为。

我已经看到计算机科学教授错误地声称Objective-C通过引用传递,因为教科书是这样说的,没有经过考虑。但它仍然只是值得传递。

答案 1 :(得分:2)

第一种方法(a)实际上正在修改通过l.append(...)传递的列表项

第二种和第三种方法(bc将参数l重新分配给新值。此重新分配是函数的本地,并且不会返回,因此它将丢失。在执行numbersb(numbers)后打印c(numbers)时,您将打印由方法a修改的值。

答案 2 :(得分:1)

Python中的大多数值都是不可变的,这意味着它们无法更新。这意味着变量的值不能改变。但是,可以将参考点设置为新的(不可变的)值。因此,在写作时

i = 4
i = 5

你实际上是首先使i指向(不可变值)4,然后将i指向(同样不可变的)值5。

但是,列表是可变的。这意味着我们可以做到

L = [3,4]
L.append(5)

并使用新值[3, 4, 5]更新相同的引用对象。

调用函数时,例如, iL不会直接传递给该函数。而是传递对值的引用。原始引用也保存在调用环境中,以便在被调用函数返回时进行恢复。

查看您的功能意味着:

def a(l):
    l.append(3)

此处,引用l 更改/更新,因为append将更改引用内容的值。因此,当从函数l返回时,将“恢复”到与函数中使用的值相同的值,并更新列表。

def b(l):
    l = 5

def c(l):
   l = (2,-2)

然而,在这两个函数中,由于赋值,引用l被指向新的东西。因此我们修改了引用本身。然后,返回时,将恢复引用以指向调用范围中使用的值。因此,l将再次成为原始列表,并且函数中使用的引用值将超出范围,该引用指向的任何值都将丢失/删除。

因此,这不仅仅是语义上的区别,而是引用称为引用通过共享调用,这必须做具有类型的不变性以及引用如何在Python中工作。