Python 核心技术与实战
景霄
Facebook 资深工程师
78765 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 47 讲
开篇词 (1讲)
Python 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

16 | 值传递,引用传递or其他,Python里参数是如何传递的?

你好,我是景霄。
在前面的第一大章节中,我们一起学习了 Python 的函数基础及其应用。我们大致明白了,所谓的传参,就是把一些参数从一个函数传递到另一个函数,从而使其执行相应的任务。但是你有没有想过,参数传递的底层是如何工作的,原理又是怎样的呢?
实际工作中,很多人会遇到这样的场景:写完了代码,一测试,发现结果和自己期望的不一样,于是开始一层层地 debug。花了很多时间,可到最后才发现,是传参过程中数据结构的改变,导致了程序的“出错”。
比如,我将一个列表作为参数传入另一个函数,期望列表在函数运行结束后不变,但是往往“事与愿违”,由于某些操作,它的值改变了,那就很有可能带来后续程序一系列的错误。
因此,了解 Python 中参数的传递机制,具有十分重要的意义,这往往能让我们写代码时少犯错误,提高效率。今天我们就一起来学习一下,Python 中参数是如何传递的。

什么是值传递和引用传递

如果你接触过其他的编程语言,比如 C/C++,很容易想到,常见的参数传递有 2 种:值传递引用传递所谓值传递,通常就是拷贝参数的值,然后传递给函数里的新变量。这样,原变量和新变量之间互相独立,互不影响。
比如,我们来看下面的一段 C++ 代码:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Python 核心技术与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(82)

  • 最新
  • 精选
  • Jingxiao
    置顶
    关于思考题: 第一题: l2和l3是指向同一个对象,因为两者之间用等号赋值了,l1并不是,l1所指向的[1, 2, 3]是另外一块内存空间,大家可以通过id()这个函数验证 第二题: 输出的是{'a': 10, 'b': 20},字典是可变的,传入函数后,函数里的d和外部的d实际上都指向同一个对象 d[idx] = value语句改变了字典对应key所指向的值
    3
    90
  • 轻风悠扬
    老师,最后一个代码示例,如果我把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]

    7
    1
  • mercy
    对象的id能否理解为指针

    作者回复: 不一样的概念呢

  • 古明地觉的编程教室
    针对@小恶魔的问题,回复一下 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}。
    8
    85
  • somenzz
    第一个比较简单,列表是可变对象,每创建一个列表,都会重新分配内存,因此 l1 和 l2 并不是同一个对象,由于 l3 = l2 表明 l3 指向 l2 的对象。 第二个 输出的结果应该是 {'a': 10, 'b': 20} ,d = {'a': 1, 'b': 2} 属于重新指向新的对象,并不改变原有的字典对象。
    23
  • 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} 3072243980
    2
    10
  • 程序员人生
    第一题,用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}应该是指向了新的对象
    1
    8
  • 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 仍然指向着刚刚被修改过的字典对象。
    7
  • 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}。
    5
  • 自由民
    总结: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/PythonPractice
    3
收起评论
显示
设置
留言
82
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部