由于没有系统的学习过Python,许多知识点都是破碎的,无法形成一个整体。总喜欢用其他语言来类比Python的语法。例如,对于参数传递,总觉得Python也会像C++那样分为值传递引用传递。无意间看到一篇博文,才发现其实区别很大。

首先,Python的函数参数传递,没有值传递和引用传递之分,因为根本没有引用传递的调用方式。

其次,就算都是值传递,Python的值传递也跟C++的值不一样。C++的值传递方式,实参的值是不会在函数调用之后有任何改变的,这点毫无疑问。而Python的值传递方式(这里姑且还是这样称呼它为值传递),实参在函数调用之后,可能不变,也可能改变,来看下面的例子:

# 例子1
def test_int(input_value):
    input_value = 2

one_value = 1
test_int(one_value)
print('one_value = {}'.format(one_value))

例子1输出为:one_value = 1,例子1的输出很好理解,那下面的例子2呢?

# 例子2
def test_list(input_list):
    input_list.append(3)

one_list = [1, 2]
test_list(one_list)
print('one_list = {}'.format(one_list))

例子2输出为:one_list = [1, 2, 3],为何结果会不一样呢?

其实,在 Python中,类型属于对象,变量是没有类型的,变量只是指向了对象:参数传递后,变量one_value和input_value都指向对象1,可以参考下面的贴图:

c python 字符串传递 python 值传递_值传递


(2和3这些对象都是系统为了效率提前产生的)。由于test_int()函数体内要将变量input_value指向对象2,然后对象1不能直接转换成对象2(可以理解为常量对象),那么只能改变变量input_value的指向,使其指向对象2,那么整个对象结构就会变成如下形式:

c python 字符串传递 python 值传递_Python参数传递_02


因此,函数调用返回时,变量one_valueinput_value指向了不同的对象。所以变量one_value的值保持不变。例子2中,变量one_listinput_list都指向同一个list对象:

c python 字符串传递 python 值传递_函数调用_03

注意list对象本身是可以改变的,向其加入一个元素不会重新创建对象,是直接在原对象添加新元素。因此调用test_func2()后,该对象结构如下图:

c python 字符串传递 python 值传递_值传递_04

one_listinput_list依旧指向同一对象,改变input_list指向对象的值也就间接的改变了one_list的指向对象的值。

归根结底,函数调用后,如果形参转向指向另外的对象,形参的改变就不会影响到实参;如果形参的改变是直接在原指向对象上进行操作,形参实参任然都会指向同一对象,形参的改变就会同步到实参上。

或者说,函数的传参,应该分为不可变对象传递可变对象传递strings, set, tuples, numbers 是不可更改的对象,而list,dict等则是可以修改的对象。函数调用为不可变对象传递时,即使函数体中修改了形参,实参的值在函数调用返回时保持不变;如果函数调用为可变对象传递,函数体中修改了形参,实参的即会跟随发生改变。

弄懂了上面的原因,下面的情况也就很好理解了:

# 例子3
def test_tuple(input_tuple):
    input_tuple += (3, 4)

one_tuple = (1, 2)
test_tuple(one_tuple)
print('one_tuple = {}'.format(one_tuple))

例子3的输出为one_tuple = (1, 2)