Python中,函数参数由实参传递给形参的过程,是由参数传递机制来控制的。根据实际参数的类型不同,函数参数的传递方式分为值传递和引用传递(又称为地址传递),本节将对这两种传递机制做深度剖析。

Python函数参数的值传递机制

所谓值传递,实际上就是将实际参数值的副本(复制品)传入函数,而参数本身不会受到任何影响。

值传递的方式,类似于《西游记》里的孙悟空,它复制一个假孙悟空,假孙悟空具有的能力和真孙悟空相同,可除妖或被砍头。但不管这个假孙悟空遇到什么事,真孙悟空都不会受到任何影响。与此类似,传入函数的是实际参数值的复制品,不管在函数中对这个复制品如何操作,实际参数值本身不会受到任何影响。

下面程序演示了函数参数进行值传递的效果:

def swap(a , b) :# 下面代码实现a、b变量的值交换a, b = b, aprint("swap函数里,a的值是", a, ";b的值是", b)
a = 6
b = 9
swap(a , b)
print("交换结束后,变量a的值是", a , ";变量b的值是", b)

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

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

从上面的运行结果来看,在 swap() 函数里,a 和 b 的值分别是 9、6,交换结束后,变量 a 和 b 的值依然是 6、9。从这个运行结果可以看出,程序中实际定义的变量 a 和 b,并不是 swap() 函数里的 a 和 b 。

正如前面所讲的,swap() 函数里的 a 和 b 只是主程序中变量 a 和 b 的复制品。下面通过示意图来说明上面程序的执行过程。

上面程序开始定义了 a、b 两个局部变量,这两个变量在内存中的存储示意图如图 1 所示。


图1.主栈区中a、b变量存储示意图图 1 .主栈区中 a、b 变量存储示意图图1.主栈区中a、b变量存储示意图
当程序执行 swap() 函数时,系统进入 swap() 函数,并将主程序中的 a、b 变量作为参数值传入 swap() 函数,但传入 swap() 函数的只是 a、b 的副本,而不是 a、b 本身。进入 swap() 函数后,系统中产生了 4 个变量,这 4 个变量在内存中的存储示意图如图 2 所示。


图2.主栈区的变量作为参数值传入swap()函数后存储示意图图 2. 主栈区的变量作为参数值传入 swap() 函数后存储示意图图2.主栈区的变量作为参数值传入swap()函数后存储示意图
当在主程序中调用 swap() 函数时,系统分别为主程序和 swap() 函数分配两块栈区,用于保存它们的局部变量。将主程序中的 a、b 变量作为参数值传入 swap() 函数,实际上是在 swap() 函数栈区中重新产生了两个变量 a、b,并将主程序栈区中 a、b 变量的值分别赋值给 swap() 函数栈区中的 a、b 参数(就是对 swap() 函数的 a、b 两个变量进行初始化)。此时,系统存在两个 a 变量、两个 b 变量,只是存在于不同的栈区中而己。

程序在 swap() 函数中交换 a、b 两个变量的值,实际上是对图 2 中灰色区域的 a、b 变量进行交换。交换结束后,输出 swap() 函数中 a、b 变量的值,可以看到 a 的值为 9,b 的值为 6,此时在内存中的存储示意图如图 3 所示。


对比图 3 与图 1,可以看到两个示意图中主程序栈区中 a、b 的值并未有任何改变,程序改变的只是 swap() 函数栈区中 a、b 的值。这就是值传递的实质:当系统开始执行函数时,系统对形参执行初始化,就是把实参变量的值赋给函数的形参变量,在函数中操作的并不是实际的实参变量。

Python函数参数的引用传递

如果实际参数的数据类型是可变对象(列表、字典),则函数参数的传递方式将采用引用传递方式。需要注意的是,引用传递方式的底层实现,采用的依然还是值传递的方式。

下面程序示范了引用传递参数的效果:

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

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

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

从上面的运行结果来看,在 swap() 函数里,dw 字典的 a、b 两个元素的值被交换成功。不仅如此,当 swap() 函数执行结束后,主程序中 dw 字典的 a、b 两个元素的值也被交换了。这很容易造成一种错觉,即在调用 swap() 函数时,传入 swap() 函数的就是 dw 字典本身,而不是它的复制品。但这只是一种错觉,下面还是结合示意图来说明程序的执行过程。

程序开始创建了一个字典对象,并定义了一个 dw 引用变量(其实就是一个指针)指向字典对象,这意味着此时内存中有两个东西:对象本身和指向该对象的引用变量。此时在系统内存中的存储示意图如图 4 所示:


图4.主程序创建了字典对象后存储示意图图4.主程序创建了字典对象后存储示意图图4.主程序创建了字典对象后存储示意图
接下来主程序开始调用 swap() 函数,在调用 swap() 函数时,dw 变量作为参数传入 swap() 函数,这里依然采用值传递方式:把主程序中 dw 变量的值赋给 swap() 函数的 dw 形参,从而完成 swap() 函数的 dw 参数的初始化。值得指出的是,主程序中的 dw 是一个引用变量(也就是一个指针),它保存了字典对象的地址值,当把 dw 的值赋给 swap() 函数的 dw 参数后,就是让 swap() 函数的 dw 参数也保存这个地址值,即也会引用到同一个字典对象。图 5 显示了 dw 字典传入 swap() 函数后的存储示意图。

图5.dw字典传入swap()函数后存储示意图图5.dw字典传入swap()函数后存储示意图图5.dw字典传入swap()函数后存储示意图
从图 5 来看,这种参数传递方式是不折不扣的值传递方式,系统一样复制了dw 的副本传入 swap() 函数。但由于 dw 只是一个引用变量,因此系统复制的是 dw 变量,并未复制字典本身。

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

为了更好地证明主程序中的 dw 和 swap() 函数中的 dw 是两个变量,在 swap() 函数的最后一行增加如下代码:

#把dw 直接赋值为None,让它不再指向任何对象
dw = None

运行上面代码,结果是 swap() 函数中的 dw 变量不再指向任何对象,程序其他地方没有任何改变。主程序调用 swap() 函数后,再次访问 dw 变量的 a、b 两个元素,依然可以输出 9、6。可见,主程序中的 dw 变量没有受到任何影响。实际上,当在 swap() 函数中增加“dw =None”代码后,在内存中的存储示意图如图 6 所示。


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

通过上面介绍可以得出如下两个结论:

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

(转载)Python函数参数传递机制(超级详细)相关推荐

  1. Python: 函数参数传递机制

    Python: 函数参数传递机制 Python函数参数由实参传递给形参的过程,是由参数传递机制来控制的,根据实际参数类型不同,函数参数传递方式分为值传递和引用传递(又称为地址传递). 值传递机制 所谓 ...

  2. python 函数参数传递机制_Python函数参数传递机制(超级详细)

    Python中,函数参数由实参传递给形参的过程,是由参数传递机制来控制的.通过学习<Python函数值传递和引用传递>一节我们知道,根据实际参数的类型不同,函数参数的传递方式分为值传递和引 ...

  3. python函数参数传递机制_Python 学习笔记(一) 理解Python的函数传参机制

    对于刚接触Python不久的新手,Python的函数传参机制往往会让人迷惑.学过C的同学都知道函数参数可以传值或者传地址.比如下面这段代码 点击(此处)折叠或打开 void func(int inpu ...

  4. Python函数参数传递:传值还是传引用

    引子 首先来看一个列子: def change(val):val.append(100)val = ['T', 'Z', 'Y'] nums = [0, 1] change(nums) print(n ...

  5. Python函数参数传递

    Python函数参数传递 一.前言 二.了解形式参数和实际参数 1.通过作用理解 2.通过比喻来理解形参和实参 三.位置参数 1.数量必须与定义时一致 2.位置必须与定义时一致 四.关键字参数 五.为 ...

  6. Python函数参数传递的几种形式

    Python函数参数传递的几种形式 前言 位置传参 序列传参(单星号元组传参) 关键字传参 字典关键字传参(双星号字典传参) 混合传参 函数的缺省参数 结束 前言 莫听穿林打叶声,何妨吟啸且徐行.竹杖 ...

  7. python类带参数_Python函数参数传递机制(超级详细)

    Python中,函数参数由实参传递给形参的过程,是由参数传递机制来控制的.通过学习<Python函数值传递和引用传递>一节我们知道,根据实际参数的类型不同,函数参数的传递方式分为值传递和引 ...

  8. import的用法python_Python导入模块,Python import用法(超级详细)

    Python导入模块,Python import用法(超级详细) 使用 Python 进行编程时,有些功能没必须自己实现,可以借助 Python 现有的标准库或者其他人提供的第三方库.比如说,在前面章 ...

  9. python 函数参数传递 格式_Python学习6.1_函数参数及参数传递

    大多数编程语言都绕不开一个名词,那就是--函数(function).而函数很重要的部分则是参数(arguments)的使用.Python的参数传递总体来说是根据位置,传递对应的参数.阐述如下: 1.位 ...

最新文章

  1. 近90%公司亏损,泡沫破裂期将至:对于AI投资人的耐心还多吗
  2. java 控制 android_Java For Android - 流程控制
  3. 安卓逆向_20 --- 模拟器检测、反调试检测、ELF动态调试、__libc_init 下断
  4. Java核心篇之多线程---day1
  5. mySQL字符串字段区别_MySQL类型之(字符串列类型区分、数据类型区分)
  6. MS Script Control的 COM
  7. Maven安装教程详解与导入
  8. 动态创建form传参
  9. 阶段3 1.Mybatis_11.Mybatis的缓存_3 mybatis一对一实现延迟加载
  10. machine learning-mnist
  11. android屏蔽电话号码,手机屏蔽骚扰电话的方法【推荐方法】
  12. 英语听力学习-VOA
  13. 解决PowerShell上运行脚本被禁用错误
  14. 实用防火与防爆技术培训---第十讲 可燃气体的燃爆特性
  15. win10兼容模式怎么设置_Win10游戏模式,你真的会用吗?只有这样设置,游戏操作才流畅...
  16. 算法【链表】 | 【链表尾部重合问题】
  17. 【渝粤题库】陕西师范大学164107 电子商务信息安全 作业(高起专)
  18. [单反一]尼克尔镜头参数解释
  19. python排序算法——归并排序(附代码)
  20. 计算机二级有重复的题目吗,计算机二级能重复考吗

热门文章

  1. 卸载VMware Server后,无法加载登录用户界面 #F#
  2. 网站“拍照”备案月底恐难实施
  3. 拒绝网络*** 如何防御Sniffer***
  4. oracle 压力测试工具benchmarksql
  5. 【python】Macbook M1/M1pro/M1max 安装anaconda记录
  6. 【Python】五子棋项目记录
  7. table()函数的使用,提高查询效率
  8. 软件公司怎么定价它们的项目_如何为副项目定价
  9. gitter 卸载_最佳Gitter渠道:Python和Django
  10. Java多线程:解决生产者/消费者模式