07--Python 函数参数传递机制

@Author : Roger TX ([email protected])
@Link : https://github.com/paotong999

一、Python 函数参数传递机制

Python 的参数值是如何传入函数的呢?这是由 Python 函数的参数传递机制来控制的。Python 中函数的参数传递机制都是“值传递”。所谓值传递,就是将实际参数值的副本(复制品)传入函数,而参数本身不会受到任何影响。

但是,需要注意的是:
当传过来的是可变类型时,我们在函数内部修改就会影响函数外部的变量。
而传入的是不可变类型时,在函数内部修改改变量并不会影响函数外部的变量。

举例如下:

def test(a_int, b_list):
    a_int = a_int + 1
    b_list.append('13')
    print('inner a_int:' + str(a_int))    # inner a_int:6
    print('inner b_list:' + str(b_list))    # inner b_list:[10, 11, '13']

if __name__ == '__main__':
    a_int = 5
    b_list = [10, 11]
    test(a_int, b_list)

    print('outer a_int:' + str(a_int))    # outer a_int:5
    print('outer b_list:' + str(b_list))    # outer b_list:[10, 11, '13']

二、Python 变量与对象

Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象。

变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的。
例如,[]是一个空列表对象,变量 a 是该对象的一个引用。

在 Python 中,「变量」更准确叫法是「名字」,赋值操作 = 就是把一个名字绑定到一个对象上。就像给对象添加一个标签。变量本身没有类型信息,类型信息存储在对象中,这和C/C++中的变量有非常大的出入(C中的变量是一段内存区域)。

函数参数
Python 函数中,参数的传递本质上是一种赋值操作,而赋值操作是一种名字到对象的绑定过程,清楚了赋值和参数传递的本质之后,现在再来分析前面两段代码。

def foo(arg):
    arg = 2
    print(arg)
  
a = 1

foo(a)    # 输出:2
print(a)    # 输出:1
赋值

在代码段1中,变量 a 绑定了 1,调用函数 foo(a) 时,相当于给参数 arg 赋值 arg=1,这时两个变量都绑定了 1。在函数里面 arg 重新赋值为 2 之后,相当于把 1 上的 arg 标签撕掉,贴到 2 身上,而 1 上的另外一个标签 a 一直存在。因此 print(a) 还是 1。

def bar(args):
    args.append(1)
 
b = []
 
print(b)# 输出:[]
print(id(b)) # 输出:4324106952
 
bar(b)
 
print(b) # 输出:[1]
print(id(b))  # 输出:4324106952
列表append

执行 append 方法前 b 和 arg 都指向(绑定)同一个对象,执行 append 方法时,并没有重新赋值操作,也就没有新的绑定过程,append 方法只是对列表对象插入一个元素,对象还是那个对象,只是对象里面的内容变了。因为 b 和 arg 都是绑定在同一个对象上,执行 b.append 或者 arg.append 方法本质上都是对同一个对象进行操作,因此 b 的内容在调用函数后发生了变化(但id没有变,还是原来那个对象)

三、可变变量的内存栈区说明

根据 Python 的参数传递机制,我们知道:传入函数的只是参数的副本,因此程序在函数中对参数赋值并不会影响参数本身。如果参数本身是一个可变对象(比如列表、字典等),此时虽然 Python 采用的也是值传递方式,但许多初学者可能会对这种类型的参数传递产生一些误会。下面程序示范了这种类型的参数传递的效果:

def swap(dw):
    # 下面代码实现dw的a、b两个元素的值交换
    dw['a'], dw['b'] = dw['b'], dw['a']
    print("swap函数里,a的值是",dw['a'], ";b的值是", dw['b'])
    dw = None
dw = {'a': 6, 'b': 9}
swap(dw)
print("交换结束后,a的值是",dw['a'], ";b的值是", dw['b'])

运行上面程序,将看到如下运行结果:

swap函数里,a的值是 9 ;b的值是 6
交换结束后,a的值是 9 ;b的值是 6

下面还是结合示意图来说明程序的执行过程
1、程序开始创建了一个字典对象,并定义了一个 dw 引用变量指向字典对象,这意味着此时内存中有两个东西:对象本身和指向该对象的引用变量


主程序创建了字典对象后存储示意图

2、接下来主程序开始调用 swap() 函数,在调用 swap() 函数时,dw 变量作为参数传入 swap() 函数,同样采用值传递方式:
把主程序中 dw 变量的值赋给 swap() 函数的 dw 形参,从而完成 swap() 函数的 dw 参数的初始化。
主程序中的 dw 是一个引用变量(也就是一个指针),它保存了字典对象的地址值,当把 dw 的值赋给 swap() 函数的 dw 参数后,就是让 swap() 函数的 dw 参数也保存这个地址值,即也会引用到同一个字典对象。


dw 字典传入 swap() 函数后存储示意图

3、当程序在 swap() 函数中操作 dw 参数时,由于 dw 只是一个引用变量,故实际操作的还是字典对象。
不管是操作主程序中的 dw 变量,还是操作 swap() 函数里的 dw 参数,其实操作的都是它们共同引用的字典对象,它们引用的是同一个字典对象。
当在 swap() 函数中交换 dw 参数所引用字典对象的 a、b 两个元素的值后,可以看到在主程序中 dw 变量所引用字典对象的 a、b 两个元素的值也被交换了。
将 swap() 函数中的 dw 赋值为 None 后存储示意图

把 swap() 函数中的 dw 赋值为 None 后,在 swap() 函数中失去了对字典对象的引用,不可再访问该字典对象。但主程序中的 dw 变量不受任何影响,依然可以引用该字典对象,所以依然可以输出字典对象的 a、b 元素的值。

四、总结

  • 不管什么类型的参数,在 Python 函数中对参数直接使用“=”符号赋值是没用的,直接使用“=”符号赋值并不能改变参数。
  • 如果需要让函数修改某些数据,则可以通过把这些数据包装成列表、字典等可变对象,然后把列表、字典等可变对象作为参数传入函数,在函数中通过列表、字典的方法修改它们,这样才能改变这些数据。

你可能感兴趣的:(07--Python 函数参数传递机制)