就我而言,Python 就像 Java 一样,事实上所有函数(方法)都是按值传递的。见this infamous answer。 “共享调用”的缺点是它让您认为不同类型正在发生不同的事情(即可变类型在函数调用中的行为与不可变类型不同)。事实并非如此,每次调用函数时都会发生同样的事情。另外,我读过的任何教科书都没有提到这个概念,我认为 pass-by-value 和 pass-by-reference 是更流行的术语。
按值传递如何工作?
它通过在函数中修改它之前复制传递给函数的“值”来工作。所以,举个例子:
my_int = 4
def pass_by_value(value):
value += 1
return value
print pass_by_value(my_int)
print my_int
这个输出:
5
4
观察my_int 的值没有改变。即使函数将传入的值递增为5,它仍保持在4。这是因为它被复制了。这里有几个微妙之处,我们将返回。忍受我。
按值传递列表
my_list = [1,2,3]
def pass_by_value_list(li):
li.append(4)
return li
print pass_by_value_list(my_list)
print my_list
这个输出:
[1, 2, 3, 4]
[1, 2, 3, 4]
什么鬼。 my_list 的值已更改。您刚才说过,按值传递会复制值!因此,my_list 不应该改变!!!
第一个例子太微妙了。我未能正确定义正在复制的“价值”。事实证明,被复制的值不是数据,而是数据的存储位置。通常 C/C++ 程序员将此称为 指针 或 地址。让我们重温一下这些示例,但稍作修改以明确重点。
按值传递如何工作? 2.0 版:
my_int = 4
def pass_by_value(value):
print "Address of parameter before += is: ", id(value)
value += 1
print "Address of parameter after += is: ", id(value)
return value
print "Address of parameter outside of the function is: ", id(my_int)
pass_by_value(my_int)
当我运行这个时,我的输出是:
Address of parameter outside of the function is: 40592528
Address of parameter before += is: 40592528
Address of parameter after += is: 40592504
貌似+=运算符之前的参数地址是40592528。这也是函数外参数的“值”!但是在+= 运算符之后,“值”在函数内部 发生了变化!但是,地址的更改并未传播到外部函数,因为地址是按值传递。函数内部的新地址是 40592504(与 40592528 不同,虽然很接近)。长话短说,+= 运算符创建了一个新的int,并且不对传入函数的地址数据进行操作。
按值传递列表,版本 2.0:
my_list = [1,2,3]
def pass_by_value_list(li):
print "Address of parameter before .append() is: ", id(li)
li.append(4)
print "Address of parameter after .append() is: ", id(li)
return li
print "Address of parameter outside of the function is: ", id(my_list)
pass_by_value_list(my_list)
这个输出:
Address of parameter outside of the function is: 110841160
Address of parameter before .append() is: 110841160
Address of parameter after .append() is: 110841160
嘿,这与整数大小写不同。看起来所有地址都是一样的!确实,这与整数情况不同。 append 函数对一个列表进行操作,并且不返回一个新列表。这就是为什么您会在函数之外看到对 my_list 的更改。 append 修改了传入函数的地址处的列表,因此我们可以看到整个程序中的数据都发生了变化。不过,没有改变的是地址。
注意:
my_list = [1,2,3]
def dont_change_the_list(li):
li.append(4)
li = []
return li
print dont_change_the_list(my_list)
print my_list
输出
[]
[1, 2, 3, 4]
换句话说,对函数内部列表的更改并没有传播到函数外部,尽管这似乎是我们之前看到的行为。这是因为li = []语句改变了li的地址,但是我们在函数执行之前复制了地址。 参数的地址是被复制的“值”,不能在函数内部改变。所以,这并没有改变主程序中my_list中的数据。
要点:Python 总是在其函数调用中使用按值传递的语义。该值恰好是传入的对象的地址,而不是传入的对象的数据。