Python中的函数参数是如何传递的?

简介: Python中的函数参数是如何传递的?

1.值传递  VS  引用传递


相信学过Java/C++等其他编程语言的小伙伴,对常见的两种函数参数传递方式(值传递和引用传递)已经不陌生啦所谓值传递就是拷贝参数的值,然后传递给函数中的新变量。这样,原来变量与新变量之间互相独立,不会产生影响。下面以C++的代码为例,分析一下值传递方式的过程:


#include <iostream>
using namespace std;
/* 值传递举例 */
// 交换两个变量的值
void swap(int x, int y)
{
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
    return;
}
int main()
{
    int a = 1, b = 100;
    cout << "交换前, a = " << a << ",b = " << b << endl;
    swap(a, b);
    cout << "交换前, a = " << a << ",b = " << b << endl;
    return 0;
}


image.gif85.png

上例中swap()函数将a和b的值拷贝了一份传递给形参x和y。因此,在swap()函数内部再交换x和y的值,x和y值互换。但是,实参a和b的值不受影响。调用swap()函数前后,a和b的值不变。


所谓引用传递是指将参数的引用传递给新的变量,原变量和新变量都会指向同一块内存地址。如果改变了其中任何一个变量的值,那么另一个变量也会随之发生改变。修改上面的C++代码,将swap()函数的形参x和y声明为引用类型:


#include <iostream>
using namespace std;
/* 引用传递举例 */
// 交换两个变量的值
void swap(int& x, int& y)
{
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
    return;
}
int main()
{
    int a = 1, b = 100;
    cout << "交换前, a = " << a << ",b = " << b << endl;
    swap(a, b);
    cout << "交换后, a = " << a << ",b = " << b << endl;
    return 0;
}


84.png

修改后的代码中,由于swap()函数的形参是引用类型。实参a、b与形参x、y一模一样,形参与实参都会指向同一块内存地址。形参x和y的任何改变都会导致a和b的相应改变。


2.Python中的变量与赋值


直接看Python代码,上菜~


a = 1
b = a
a = a + 1

上面的代码虽然简单,里面却包含了python语言的一些基本原理。首先,将1赋值给a,换言之就是a指向了1这个整型对象。如下图所示:


image.gif83.png

接着,b=a表示将变量b也同时指向1这个整型对象。注意:python中的对象可以被多个变量所指向或引用。

image.gif82.png


最后,执行a = a + 1。注意:在python中像整型、浮点型、字符串等都是不可变对象。所以,a = a + 1并不是让a的值自增1,而是表示重新创建一个新的值为2的整型对象,并让变量a指向它。但是,变量b的指向仍不变,仍然执行1这个整型对象。PS:可以使用Python中的内建函数id()来判断两个对象是否相同。

81.png

通过上面的一波解释,相信聪明的小伙伴们已经知道:a和b开始只是两个指向同一个整型对象的变量而已。简单的赋值语句b = a,并不表示重新创建了新的对象,只是让同一个对象被多个变量指向或引用而已。


此外,指向同一个对象也并不意味着两个变量就会被绑定在一起。如果你给其中一个变量重新赋值,并不会影响其他变量的值。理解了上面简单的变量赋值例子,下面再来看一个可变对象列表的例子:


l1 = [1, 2, 3]
l2 = l1
l1.append(4)
print("l1:", l1)
print("l2:", l2)


80.png


与上一段代码类似,首先让变量l1和l2同时指向列表对象[1,2,3]。由于列表是可变对象,因此l1.append(4)不会创建一个新的列表,仅仅是在原列表的末尾插入一个元素4,变成[1,2,3,4]。由于l1和l2变量同时指向这个列表对象,所以列表的变化会同时反映在两个变量上。最后,l1和l2的值都同时变成[1,2,3,4]。


79.png


此外,还需要注意:Python中的变量可以被删除,但对象无法被删除。如下面的代码段所示:


l1 = [1, 2, 3]
del l1


del l1删除了l1这个变量,从此之后你无法访问l1了。但是,列表对象[1,2,3]仍然存在。python程序运行时,其自带的垃圾回收机制会跟踪每个对象的引用。如果[1,2,3]这个列表对象除l1外,还在别的地方被引用。那么,[1,2,3]就不会被回收。反之,则会被回收。


对以上内容进行总结,有如下结论:


(1).变量的赋值,只是表示让变量指向了某个对象,并不表示拷贝对象的值给变量。此外,一个对象,可以被多个变量同时所指向。

(2).可变对象(如:列表、字典、集合等)的改变,会影响所有指向该对象的变量。

(3).对不可变对象(如:字符串、元组、整型等)来说,所有指向这些对象的变量的值总是一样的,不会发生改变。但是,可以通过某些操作(如:+=等)更新不可变对象的值时,会返回一个新的对象。

(4).变量可以被删除,但对象无法被删除。


3.Python中函数的参数传递


首先,引用Python官方文档中的一段话:


Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per se.


上面的这段话,翻译过来就是说Python中的参数传递是赋值传递或者称为对象的引用传递。由于Python中所有的数据类型实质都是对象,因此在参数传递时,只是让新变量与原变量指向相同的对象而已,并不存在值传递或引用传递这个说法。下面以一个简单的例子来分析一下:


def foo(b):
    b = 2
a = 1
foo(a)
print("a =", a)  # a = 1


在上面的代码段中,函数foo()在进行参数传递时,让变量a和b同时指向了1这个整型对象。但是,当执行到b=2时,系统又会创建一个值为2的整型对象,并让变量b指向它。然而,变量a仍然指向的是1这个整型对象。所以,最终的结果是a = 1。


有的小伙伴们可能就会问,如果我就是想改变变量a的值,那又改咋办呢?方法当然是有的,只需要让函数foo()返回新的变量并赋值给a。这样,变量a就指向了一个新的整型对象。


def foo(b):
    b = 2
    return b
a = 1
a = foo(a)
print("a =", a)  # a = 2


此外,值得注意的是:当可变对象作为函数参数传入入函数内部时,改变可变对象的值,就会影响所有指向它的变量。如下面的例子所示:


def bar(l2):
    l2.append(4)
l1 = [1, 2, 3]
bar(l1)
print("l1 =", l1) # [1, 2, 3, 4]


上面的代码段中,l1和l2两个变量同时指向值为[1,2,3]的列表对象。由于列表是可变对象,执行append()函数时,在列表对象末尾插入新元素4,变量l1和l2的值都会发生变化。但是,下面这个例子就比较特殊,看似是在列表的末尾增加一个新元素,实质却得到明显不同的结果


def func(l2):
    l2 = l2 + [4]
l1 = [1, 2, 3]
func(l1)
print("l1 =", l1)  # [1, 2, 3]


为啥上面代码段输出l1的结果仍然是[1,2,3]呢?为啥不会是[1,2,3,4]呢?原因在于l2 = l2 + [4]表示创建了一个末尾插入元素4的新列表对象,并且让变量l2指向这个新的列表对象。前面的整个过程与变量l1无关,因此l1的值不会改变。同样地,如果要改变l1的值,就必须让变量l1指向函数func()返回的新列表对象。如下面的代码段所示:


def func(l2):
    l2 = l2 + [4]
    return l2
l1 = [1, 2, 3]
l1 = func(l1)
print("l1 =", l1) # [1, 2, 3, 4]


4.总结


与其他编程语言不同,Python中的参数传递既不是值传递,也不是引用传递,而是赋值或者叫对象的引用传递:不是指向一个具体的内存地址,而是指向一个具体的对象。如果是可变对象,当其改变时,所有指向这个对象的变量都会发生改变;如果是不可变对象,当其发生改变时,简单的赋值运算只会改变其中一个变量的值,其余变量不会受到影响

目录
打赏
0
0
0
0
7
分享
相关文章
|
4天前
|
Python format()函数高级字符串格式化详解
在 Python 中,字符串格式化是一个重要的主题,format() 函数作为一种灵活且强大的字符串格式化方法,被广泛应用。format() 函数不仅能实现基本的插入变量,还支持更多高级的格式化功能,包括数字格式、对齐、填充、日期时间格式、嵌套字段等。 今天我们将深入解析 format() 函数的高级用法,帮助你在实际编程中更高效地处理字符串格式化。
49 0
|
1月前
|
分析参数顺序对Python requests库进行POST请求的影响。
最后,尽管理论上参数顺序对POST请求没影响,但编写代码时仍然建议遵循一定的顺序和规范,比如URL总是放在第一位,随后是data或json,最后是headers,这样可以提高代码的可读性和维护性。在处理复杂的请求时,一致的参数顺序有助于调试和团队协作。
95 9
Python 的内建函数
Python 的内置函数列表,方便查询使用方法。
Python内置函数ord()详解
`ord()` 是 Python 中用于将单个字符转换为对应 Unicode 码点的核心函数,支持 ASCII、多语言字符及特殊符号。其返回值为整数(范围 0-1114111),适用于字符编码验证、数据清洗、自定义排序、基础加解密等场景。使用时需注意参数长度必须为 1,否则会触发 `TypeError`。结合 `chr()` 函数可实现双向转换,进阶技巧包括多字节字符处理、编码范围检测及字符分类验证等。
[oeasy]python091_列表_索引_index_中括号_索引函数
本文介绍了Python中列表与字符串的索引及index函数用法。通过range生成列表,使用索引[]访问和修改列表元素,index函数查找元素位置。字符串支持索引访问但不可直接修改。还探讨了16进制数在Python中的表示方法,以及日期、月份等特殊字符的Unicode范围。最后总结了列表与字符串操作的区别,并预告后续内容,提供蓝桥云课、GitHub和Gitee链接供进一步学习。
88 20
|
3月前
|
[oeasy]python086方法_method_函数_function_区别
本文详细解析了Python中方法(method)与函数(function)的区别。通过回顾列表操作如`append`,以及随机模块的使用,介绍了方法作为类的成员需要通过实例调用的特点。对比内建函数如`print`和`input`,它们无需对象即可直接调用。总结指出方法需基于对象调用且包含`self`参数,而函数独立存在无需`self`。最后提供了学习资源链接,方便进一步探索。
89 17
[oeasy]python083_类_对象_成员方法_method_函数_function_isinstance
本文介绍了Python中类、对象、成员方法及函数的概念。通过超市商品分类的例子,形象地解释了“类型”的概念,如整型(int)和字符串(str)是两种不同的数据类型。整型对象支持数字求和,字符串对象支持拼接。使用`isinstance`函数可以判断对象是否属于特定类型,例如判断变量是否为整型。此外,还探讨了面向对象编程(OOP)与面向过程编程的区别,并简要介绍了`type`和`help`函数的用法。最后总结指出,不同类型的对象有不同的运算和方法,如字符串有`find`和`index`方法,而整型没有。更多内容可参考文末提供的蓝桥、GitHub和Gitee链接。
80 11
Python中main函数:代码结构的基石
在Python中,`main`函数是程序结构化和模块化的重要组成部分。它实现了脚本执行与模块导入的分离,避免全局作用域污染并提升代码复用性。其核心作用包括:标准化程序入口、保障模块复用及支持测试驱动开发(TDD)。根据项目复杂度,`main`函数有基础版、函数封装版、参数解析版和类封装版四种典型写法。 与其他语言相比,Python的`main`机制更灵活,支持同一文件作为脚本运行或模块导入。进阶技巧涵盖多文件项目管理、命令行参数处理、环境变量配置及日志集成等。此外,还需注意常见错误如全局变量污染和循环导入,并通过延迟加载、多进程支持和类型提示优化性能。
267 0
Python入门:8.Python中的函数
### 引言 在编写程序时,函数是一种强大的工具。它们可以将代码逻辑模块化,减少重复代码的编写,并提高程序的可读性和可维护性。无论是初学者还是资深开发者,深入理解函数的使用和设计都是编写高质量代码的基础。本文将从基础概念开始,逐步讲解 Python 中的函数及其高级特性。
Python入门:8.Python中的函数
Python学习:内建属性、内建函数的教程
本文介绍了Python中的内建属性和内建函数。内建属性包括`__init__`、`__new__`、`__class__`等,通过`dir()`函数可以查看类的所有内建属性。内建函数如`range`、`map`、`filter`、`reduce`和`sorted`等,分别用于生成序列、映射操作、过滤操作、累积计算和排序。其中,`reduce`在Python 3中需从`functools`模块导入。示例代码展示了这些特性和函数的具体用法及注意事项。

热门文章

最新文章

推荐镜像

更多
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问