想要了解 Python 的参数是如何传递的,首先要知道 python 的变量的内存管理机制。
不同的编程语言的内存分配策略有时是大相径庭,在 C 语言中分配内存时:
int a = 1;
现在想象下面这种情形:将 1 放入一个变量名为 a 的水杯中,这个水杯作为 1 的载体。上面这句语句就是创建出一块内存区域(a)来存储变量(水杯)的值(1)。
a = 2;
如果想要改变水杯中的值,只需要将想要修改的值(2)直接放入水杯中替换之前的1就完成了重新赋值。
int b = a;
将一个变量的值赋予其他的变量则会开辟新的存储空间,并复制当前水杯中的值,然后放入新的水杯。
---------------------------
而在 Python中变量的内存分配很诡异(至少对于我来说),变量不再是水杯了,而更像一个标签。在 Python中分配内存时:
a = 1
Python 这时将一个名为 a 的标签绑定到变量值 1 上。
a = 2
正如你所看到的,在修改变量值时,标签 a 是直接将自己绑定到内存中的值 2 上。
b = a
如果增加另一个变量 b 的值也为 2,标签 b 也会像 a 一样一起绑定到数值 2 上。
---------------------------
现在就可以讲讲参数传递的问题:
Python 中姑且可以说有两种函数变量传递的方式,一种称为 Call-by-value,另一种是 Call-by-reference(就是题主说的引用传递)。首先说说 call-by-value,在 call-by-value 中,参数表达式会被 Python 解释并绑定到函数中对应的变量中。所以如果参数表达式是一个变量,在函数中则会复制该变量的值然后再使用这个复制的值。因此这个变量值在函数外部作用域完全不会被改变。
在 call-by-reference 中:函数会直接使用一个值的隐含式的引用,而不是像 call-by-value 直接使用另外复制出的值,这样做的后果就是,在函数内部修改同名变量的值时导致外部的同名变量跟着改变。但这个传递方式也有很突出的优点:不论在时间还是内存空间效率都很高,因为不用另外复制变量。注意:严格意义上来说 Python 传参策略既不是 call-by-reference,也不是 call-by-value,而是另一种机制 call-by-object,亦 call-by-Object-reference ,或 call-by-sharing(参数传递是对一个对象的引用,但是这个引用却又是 passed by value)。根据不同的情况使用不同的机制,所以只是在这里用这两种方式来进行更确切地说明。
---------------------------
接下来看几个具体的示例:
下面是一个很让人费解的示例:
def a(the_list):
print('Got', the_list)
the_list.append('treats')
print('Set to', the_list)
outer_list = ['Dogs', 'eats']
print('Before, outer_list = ', outer_list)
a(outer_list)
print('After, outer_list = ', outer_list)
# Outputs in terminal
# >>> Before, outer_list = ['Dogs', 'eats']
# >>> Got ['Dogs', 'eats']
# >>> Set to ['Dogs', 'eats', 'treats']
# >>> After, outer_list = ['Dogs', 'eats', 'treats']
为什么会这样?因为 `the_list` 就是对 `list['Dogs', 'eats']` 的引用,而不是复制。并且在 Python 中 Object 对象是 mutable 可变对象(String等为不可变对象),所以 `append()` 方法能够改变 `the_list` 中的值。
理解上面的示例后,看下面更进阶的一个示例:
def b(the_list):
print('Got', the_list)
the_list = ['You', 'never', 'lie']
print('Set to', the_list)
outer_list = ['Dogs', 'eats']
print('Before, outer_list = ', outer_list)
a(outer_list)
print('After, outer_list = ', outer_list)
# Outputs in terminal
# >>> Before, outer_list = ['Dogs', 'eats']
# >>> Got ['Dogs', 'eats']
# >>> Set to ['You', 'never', 'lie']
# >>> After, outer_list = ['Dogs', 'eats']
为什么在这个例子中之前所说的不管用了呢?the_list 刚进入函数时,确实是对变量的引用,但是当 the_list = ['You', 'never', 'lie'] 这句语句执行之后,相当于直接在函数中又创建了一个新的局部变量,名字也叫 the_list, 本质上已经不同于参数the_list 。因为它将 the_list 绑定到了list['You', 'never', 'lie'] 上(想想之前所说的变量内存分配机制图,the_list 就像一个标签,直接绑定到了list['You', 'never', 'lie']上),所以自然也就没有改变 outer_list的值.
希望以上的回答能够解决题主对参数传递的疑惑。如有认知错误,欢迎指出。