第115天:Python 到底是值传递还是引用传递

简介: 第115天:Python 到底是值传递还是引用传递

我们平时写的 Python 程序中充斥着大量的函数,包括系统自带函数和自定义函数,当我们调用函数时直接将参数传递进去然后坐等接收返回值即可,简直不要太好用。那么你知道函数的参数是怎么传递的么,是值传递还是引用传递呢,什么又是值传递和引用传递呢?


这个问题对于很多初学者还是比较有难度的,看到这里你可以稍加停顿,自己思考一下,看看自己是否真正理解了。很多人只是知道概念但是让他说他又说不清楚,思考过后如果你还觉得模糊的话,往下仔细看,我今天就带着你深入剖析下函数的参数传递机制。


为了搞清楚函数的参数传递机制,你必须先彻底理解形参和实参。例如下面的 sayHello 函数,括号里面的 name 就是形参,而当调用函数时传递的 name 是实参。


def sayHello(name): # name 是形式参数    print("Hello %s" % name)
name = "hanmeimei" # name 是实际参数sayHello(name)
# 输出结果Hello hanmeimei


值传递 OR 引用传递


上面我们说了,当调用函数时我们会把实际参数传递给形式参数。而这个传递过程有两种,就是我们上文说的值传递和引用传递了。


顾名思义,所谓值传递就是指在传递过程中将实际参数的值复制一份传递给形式参数,这样即使在函数执行过程中对形式参数进行了修改,形式参数也不会有所改变,因为二者互不干扰。而引用传递是值将实际参数的引用传递给实际参数,这样二者就会指向同一块内存地址,在函数执行过程中对形式参数进行了修改,形式参数也会一并=被修改。

为了故事的顺利发展,我们先来看看 Python 中关于变量的赋值。



>>> a = 10>>> b = a>>> a = a + 10>>> a20>>> b10>>>


在上述的例子中,我们声明了一个变量 a,其值为 10,然后将 b 也指向 a,这是在内存中的布局是这样的,变量 a 和 b 会指向同一个对象 10,而不是给 b 重新生成一个新的对象。


image.png


由此可知,同一个对象是可以被多个对象引用的。


当执行完 a = a + 10 后,因为整数是不可变对象,所以并不会将 10 变成 20,而是生成一个新的对象 20 ,然后 a 会指向这个新的对象。b 还是指向旧对象 10。


image.png


所以,最后就是 a 为 20,而 b 为 10。


理解了上面的赋值过程之后,我们再来看看参数的传递。老规矩,还是直接看例子吧,代码是不会骗人的。


def swap(a, b):    a, b = b, a    print("in swap a = %d and b = %d " % (a, b))
a = 100b = 200swap(a, b)print("in main a = %d and b = %d " % (a, b))
## 输出结果in swap a = 200 and b = 100 in main a = 100 and b = 200


我们在函数 swap 中交换 a 和 b 的值,然后分别在主函数和 swap 函数中输出其结果,


由结果可知,swap 函数并不会改变实际参数 a,b 的值,因此我们可以得出结论,Python 函数参数是按照值传递的。


别急,不妨再看多一个例子。


def swap(list):    list.append(4)    print("in swap list is %s " % list)
list_x = [1, 2, 3]swap(list_x)print("in main list is %s " % list_x)
## 输出结果in swap list is [1, 2, 3, 4] in main list is [1, 2, 3, 4]


咦,值被改了,这不就是引用传递了么。于是,我们又得出结论,Python 函数参数是按照引用传递的。


这未免有点太不严谨了,事实上我们上面的第二个例子有点不太严谨,我稍微修改了下 swap 函数,咱们在看看测试结果。



def swap(list):    list = list + [4]    print("in swap list is %s " % list)
## 输出结果in swap list is [1, 2, 3, 4] in main list is [1, 2, 3]


我们只是更改了 swap 函数内一行代码,结果就完全不一样了。为了更好的理解其执行过程,我画了张图。


image.png


在第一个关于 list 的例子中,我们首先声明了一个列表,其中的元素为 [1,2,3],此时其内存布局如上图中的步骤一所示。list_x 指向内存地址为 OX7686934F 的区域。


当调用 swap 函数将 list_x 传递给形式参数 list 时,会将该地址直接传递过去,list 也会指向这个地址,如步骤二所示。


最后,由于列表是可变的,所以当 list 在向列表中添加元素时,list_x 自然会受到影响,因为二者指向的是同一块内存。


所以,这里也是值传递,只不过传递的值是对象的内存地址罢了。


第二个关于 list 的例子中,我们对 swap 函数进行了修改,其执行流程如下图所示。


image.png


在执行 swap 函数之前都与上面的例子毫无差别。在 swap 函数内部 list = list + [4] 表示新建一个末尾加入元素 4 的新的列表,并让 list 指向这个新的内存地址  OX7686936A。因为是生成了一个新的对象,与原对象无关,所以 list_x 不受影响。


简而言之,弄清楚改变变量和重新赋值的区别就好了,第一个例子中我们改变了变量的值,所以当函数执行结束后所有指向该对象的变量都会受影响。而重新赋值相当于重新生成一个新的对象并在新的对象上做操作,因此旧对象不受影响。


如果我们要想在函数中改变对象,第一可以传入可变数据类型(列表,字典,集合),直接改变;第二还可以创建一个新的对象,修改后返回。建议用后者,表达清晰明了,不易出错。


总结


本文介绍了 Python 函数的参数传递机制。理解了参数的传递过程和底层实现细节,写代码时将会少犯一些不必要的低级错误。


最后,无论是值传递还是引用传递,我们只需关注函数内部是否会生成新的对对象即可。凡是对原对象操作的函数,都会影响传递的实际参数;凡是生成了新对象的操作,都不会影响传递的实际参数。


目录
相关文章
|
10月前
|
Python
python之函数的参数传递(引用传递和值传递),查看变量的内存地址的方法
python之函数的参数传递(引用传递和值传递),查看变量的内存地址的方法
|
存储 C++ Python
Python到底是引用传递还是值传递?网上大多数教程都讲错了
或许在很多人的直观印象中,变量是一个容器;给变量赋值,就像是往一个存储的容器中填入一个数据;再次赋值就是把容器中的数据换掉。
|
存储 C++ 开发者
python|基础|值传递和引用传递
python|基础|值传递和引用传递
136 0
|
7天前
|
算法 程序员 开发工具
百万级Python讲师又一力作!Python编程轻松进阶,豆瓣评分8.1
在学习Python的旅程中你是否正在“绝望的沙漠”里徘徊? 学完基础教程的你,是否还在为选择什么学习资料犹豫不决,不知从何入手,提高自己?
百万级Python讲师又一力作!Python编程轻松进阶,豆瓣评分8.1
|
5天前
|
算法 程序员 开发工具
百万级Python讲师又一力作!Python编程轻松进阶,豆瓣评分8.1
在学习Python的旅程中你是否正在“绝望的沙漠”里徘徊? 学完基础教程的你,是否还在为选择什么学习资料犹豫不决,不知从何入手,提高自己?
|
2天前
|
数据采集 存储 人工智能
掌握Python编程:从基础到进阶的实用指南
【8月更文挑战第17天】 本文旨在通过浅显易懂的语言和实际案例,为初学者和有一定基础的开发者提供一条清晰的Python学习路径。我们将从Python的基本语法入手,逐步深入到面向对象编程、数据科学应用及网络爬虫开发等高级主题。每个部分都配备了代码示例和实操建议,确保读者能够将理论知识转化为实际能力。无论你是编程新手,还是希望提升Python技能的开发者,这篇文章都将为你打开一扇通往高效编程世界的大门。
7 2
|
7天前
|
Python
python Process 多进程编程
python Process 多进程编程
17 1
|
11天前
|
存储 数据挖掘 程序员
揭秘Python:掌握这些基本语法和数据类型,你将拥有编程世界的钥匙!
【8月更文挑战第8天】Python是一种高级、解释型语言,以简洁的语法和强大的功能广受好评。本文从基本语法入手,强调Python独特的缩进规则,展示清晰的代码结构。接着介绍了Python的主要数据类型,包括数值、字符串、列表、元组、集合和字典,并提供了示例代码。通过这些基础知识的学习,你将为深入探索Python及其在文本处理、数据分析等领域的应用打下坚实的基础。
26 3
|
13天前
|
Python
揭秘!Python系统编程里那些让代码自由穿梭的神奇代码行
【8月更文挑战第6天】在Python编程中,一些简洁有力的代码构造让程序更加灵动高效。列表推导式能一行生成列表,如`squares = [x**2 for x in range(10)]`。`with`语句确保资源自动释放,例`with open('example.txt', 'r') as file:`。`lambda`函数便于快速定义小函数,`map(lambda x: x + 1, numbers)`即可完成列表映射。
28 4
|
13天前
|
API C语言 开发者
Python如何成为跨平台编程的超级巨星:系统调用深度探索
【8月更文挑战第6天】Python凭借简洁的语法和强大的库支持,在编程领域中脱颖而出。其跨平台特性是基于CPython等解释器的设计理念,使得Python程序能在不同操作系统上运行而无需修改代码。Python标准库提供的抽象层隐藏了系统间的差异,加之ctypes等扩展机制,使开发者能高效地编写兼容性强且性能优异的应用。例如,在Windows上利用ctypes调用GetSystemTime系统API获取当前时间,展现了Python深入系统底层的强大能力和灵活性。随着技术演进,Python将继续巩固其作为首选编程语言的地位。
19 3