16 | 值传递,引用传递or其他,Python里参数是如何传递的?
景霄
该思维导图由 AI 生成,仅供参考
你好,我是景霄。
在前面的第一大章节中,我们一起学习了 Python 的函数基础及其应用。我们大致明白了,所谓的传参,就是把一些参数从一个函数传递到另一个函数,从而使其执行相应的任务。但是你有没有想过,参数传递的底层是如何工作的,原理又是怎样的呢?
实际工作中,很多人会遇到这样的场景:写完了代码,一测试,发现结果和自己期望的不一样,于是开始一层层地 debug。花了很多时间,可到最后才发现,是传参过程中数据结构的改变,导致了程序的“出错”。
比如,我将一个列表作为参数传入另一个函数,期望列表在函数运行结束后不变,但是往往“事与愿违”,由于某些操作,它的值改变了,那就很有可能带来后续程序一系列的错误。
因此,了解 Python 中参数的传递机制,具有十分重要的意义,这往往能让我们写代码时少犯错误,提高效率。今天我们就一起来学习一下,Python 中参数是如何传递的。
什么是值传递和引用传递
如果你接触过其他的编程语言,比如 C/C++,很容易想到,常见的参数传递有 2 种:值传递和引用传递。所谓值传递,通常就是拷贝参数的值,然后传递给函数里的新变量。这样,原变量和新变量之间互相独立,互不影响。
比如,我们来看下面的一段 C++ 代码:
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
Python中的参数传递机制是程序员需要了解的重要知识之一。本文首先介绍了值传递和引用传递在其他编程语言中的概念和实现方式,然后深入探讨了Python中的变量赋值原理。文章指出,Python中的参数传递既不是纯粹的值传递,也不是纯粹的引用传递,而是一种“对象引用传递”的机制。通过对Python变量及其赋值的基本原理的解释,读者可以清晰地了解Python中参数传递的工作方式。文章以简洁明了的语言和具体的示例,帮助读者快速理解Python中参数传递的特点和原理。 在Python中,参数传递是赋值传递,或者叫作对象的引用传递。所有的数据类型都是对象,因此参数传递时,只是让新变量与原变量指向相同的对象而已,并不存在值传递或是引用传递一说。当可变对象作为参数传入函数时,改变可变对象的值会影响所有指向它的变量;而对于不可变对象,简单的赋值只能改变其中一个变量的值,其余变量不受影响。如果想通过函数改变某个变量的值,通常有两种方法:直接在可变数据类型上修改,或者创建一个新变量保存修改后的值,然后将其返回给原变量。 文章还留下了两道思考题,引发读者思考。通过本文的总结,读者可以快速了解Python中参数传递的特点和原理,以及如何在实际工作中应用这些知识。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Python 核心技术与实战》,新⼈⾸单¥59
《Python 核心技术与实战》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(82)
- 最新
- 精选
- Jingxiao置顶关于思考题: 第一题: l2和l3是指向同一个对象,因为两者之间用等号赋值了,l1并不是,l1所指向的[1, 2, 3]是另外一块内存空间,大家可以通过id()这个函数验证 第二题: 输出的是{'a': 10, 'b': 20},字典是可变的,传入函数后,函数里的d和外部的d实际上都指向同一个对象 d[idx] = value语句改变了字典对应key所指向的值2019-06-17397
- 轻风悠扬老师,最后一个代码示例,如果我把l2 = l2 + [4] 换成 l2 += [4],l1会变成[1,2,3,4].不明白l2 = l2 + [4] 换成 l2 += [4]有什么不一样的地方,有点小困惑 def my_func5(l2): l2 += [4] l1 = [1, 2, 3] my_func5(l1) l1 [1, 2, 3, 4]
作者回复: l2 = l2 + [4]和l2 += [4]是一样的啊,程序运行的结果都是[1, 2, 3, 4]
2020-05-1072 - mercy对象的id能否理解为指针
作者回复: 不一样的概念呢
2019-11-121 - 小云同学针对@小恶魔的问题,回复一下 python里面一切皆对象, 比如a=1。在java里面是int a = 1,相当于先声明了一个int类型的变量a,然后给这个变量赋值为1。但在python中,是先在内存中申请一份空间,存的值为1,然后再给这块空间贴上一个标签,叫变量a,因此python中变量实际上是一个便利贴,可以贴在任何地方。并且还可以通过值来推断出变量的类型,这一步是由解释器来完成的。所以python虽然不需要显式声明变量,但它其实是强类型语言。 def func(d): d['a'] = 10 d['b'] = 20 d = {'a': 1, 'b': 2} d = {} func(d) print(d) # {'a': 10, 'b': 20} 至于这里为什么会是这个结果,当我们将d传递给func的时候,其实func里面的d和外面的d指向的是同一片内存。相当于一开始d={},存放{}这份空间只有d这一个便利贴,但是func(d)的时候,这份空间又多了一个便利贴。尽管都叫d,但一个是全局变量d,一个是函数的参数d 当d['a'] = 10和d['b']=20的时候,由于字典是可变类型,所以外面的d也被修改了,此时外面的d和函数里面的d都指向了{'a': 10, 'b': 20}, 但是当d = {'a': 1, 'b': 2}的时候,这是属于赋值。因此python会在内存中再开辟一份空间,空间存放{'a': 1, 'b': 2},然后让函数里面的局部变量d指向它,相当于将原本位于{'a':10,'b':20}上的便利贴撕下来,贴在了另一块空间。但这只是函数里面的d,对外面的d是没有影响的,所以外面的d依旧是{'a': 10, 'b': 20}。2019-06-14888
- somenzz第一个比较简单,列表是可变对象,每创建一个列表,都会重新分配内存,因此 l1 和 l2 并不是同一个对象,由于 l3 = l2 表明 l3 指向 l2 的对象。 第二个 输出的结果应该是 {'a': 10, 'b': 20} ,d = {'a': 1, 'b': 2} 属于重新指向新的对象,并不改变原有的字典对象。2019-06-1424
- yshan首先更正下,需要先定义d={}。 然后,局部变量与全局变量的区别,函数内定义的d为全局变量,在没有关键字声明的情形下不能改变全局变量,由于字典可变,遵循可变则可变的原则,输出为{'a': 10, 'b': 20}。 最后,看实验: def func(d): print(id(d)) d['a'] = 10 d['b'] = 20 print(id(d)) d = {'a':1, 'b':2} print(id(d)) print(d) d = {} print(id(d)) func(d) print(d) print(id(d)) 执行结果: 3072243980 3072243980 3072243980 3072244108 {'a': 1, 'b': 2} {'a': 10, 'b': 20} 30722439802019-06-16211
- 程序员人生第一题,用id()打印出来后可以证明,l1和l2不是同一个对象,l2和l3是同一个对象。由于列表是可变的,所以l1和l2指向不同的内存区域。 第二题,做了一下修改,如下: def func(d): d['a'] = 10 d['b'] = 20 d={'a':1,'b':2} d={} func(d) print(d) 执行结果: {'a': 10, 'b': 20} d = {'a': 1, 'b': 2}应该是指向了新的对象2019-06-1418
- Wing·三金# C++ - 按值传递:拷贝参数的值构建新的变量传递到函数 - 按引用传递:把参数的引用(i.e. 地址)传递到函数 # Python - 按赋值传递/按对象的引用传递 - 凡是对对象本身进行的操作,都会影响传递的原对象;凡是生成了新对象的操作,都不会影响传递的原对象 - 正如【一个人可以死两次,第一次是肉体死去,第二次是当没人记得它的时候】,python 中如果有多个变量指向同一个对象,那么当删除一个变量时并不会真正删除其所指定的对象;只有当所有指定该对象的变量都被删除时,python 才会回收该对象所占用的资源 - 一般原则:对于不可变的数据类型,operator 等操作会返回新的对象,不会影响原对象;对于可变的数据类型,任何对【对象本身】的操作都会影响所有指向该对象的变量 - 补充上一条:e.g. 对于 list 而言,l += [1] 和 l = l + [1] 不同!前者是在 l 本身的末尾添加新元素,后者是在 l 的基础上添加新的元素并返回新的对象 - 在工程上,偏爱类似于上一条后者的作法——即通过【创建新的对象+将其返回】的作法,来减少出错的概率 # 思考题 1. l1 与 l2 不同,l3 与 l2 同; 2. 严格来说,如果没有上下文,这是一段错误的代码,因为没有预先定义 d 变量;不妨假设在第 6 行之前补充语句 d = {},则输出结果为 {'a': 10, 'b': 20},因为 func 中前两行才是改变了对象的操作。而第 3 行只是将函数中的局部变量 d 指向了新的字典 {'a': 1, 'b': 2},但全局变量 d 仍然指向着刚刚被修改过的字典对象。2019-06-167
- SCAR第一题:l2和l3指向同一个对象,l2和l1不指向同一个对象。这个题的关键要点是要了解list对象是没有“内存驻留”机制的,这点和整数对象对小于256的数采用的“内存驻留”是截然不同的,所以l1和l2不是指向同一对象。而l3=l2,这就是让l3指向l2指向的对象,很显然l3和l2指向的是同一个对象。 第二题:题目里的d = {'a': 1, 'b': 2}应该是顶格的吧,估计是老师手误或是编辑器出问题了,不然没意义。如果是这样,print(d),输出应该是{'a': 10, 'b': 20}。2019-06-145
- 自由民总结:Python中参数传递既不是传值也不是传引用,而是赋值传递,或传对象的引用。不是指向一个具体的内存地址,而是指向具体的对象。 如果对象是不变的,改变对象会新建一个对象,并将其中一个变量指向该对象,其它变量不变。如果对象是可变的,改变一个变量时,其它所有指向该对象的变量都会受影响。要想在函数中改变对象,可以传入可变数据类型(列表,字典,集合),直接改变;也可以创建一个新对象,修改以后返回。建议用后者,表达清晰明了,不易出错。 思考题1: l2与l3指向同一对象,与l1不同。 # 思考题1 l1 = [1,2,3,4] l2 = [1,2,3,4] l3 = l2 print(id(l1), id(l2), id(l3)) 思考题2 {"a":10, "b":20} 课程的练习代码: https://github.com/zwdnet/PythonPractice2019-10-033
收起评论