8.Python函数进阶-2
8. 函数进阶 - 2
8.1 函数参数的高级用法
缺省参数
引入
缺省参数也叫做默认参数,是指定义函数时形参变量有默认值,如果调用函数时没有传递参数,那么函数就用默认值,如果传递了参数就用传递的那个数据。
示例:
1 | def print_info(name, age=35): |
缺省参数的作用
当调用函数时,有些参数不必传递,而是用默认值,这样的场景往往都用缺省参数
例如,一个学校现在开始检查每个学生的信息,学生说:报告老师我是xxx学校xxx系xxx年级xxx班学生,名字叫xxxx,大家想只要是这学校的学生那么“xxx学校”就可以省略不用说了,因为大家都知道。所以就可以认为默认的学校就是xxx,而当其他学校的学生介绍时yyy学校名字就一定要说清楚,否则让人弄混了。
示例:
1 | def print_info(name, class_name, grade, department_name, school_name="东莞理工学院"): |
注意点
- 缺省参数只能在形参的最后(即最后侧)
- 缺省参数全挨在一起(在右侧),不是缺省参数挨在一起(在左侧)
1 | def printinfo(name, age=35, sex): |
命名参数
引入
命名参数是指:在调用函数时,传递的实参带有名字,这样的参数叫做命名参数
示例:
1 | def test(a, b): |
命名参数的作用
命名参数能够在调用函数的时候,不受位置的影响,可以给需要的参数指定传递数据
注意点
- 命名参数的名字要与形参中的名字相同,不能出现命名参数名字叫做
num,而形参中没有变量num - 如果形参左侧有普通的形参,调用函数时传递的参数一定要先满足这些形参,然后再根据需要编写命名参数
1 | def test(a, b, c=100, d=200): |
不定长参数
引入
不定长参数:定义函数的时候形参可以不确定到底多少个,这样的参数就叫做不定长参数
不定长参数有2种方式表示
*args:表示调用函数时多余的未命名参数都会以元组的方式存储到args中**kwargs:表示调用函数时多余的命名参数都会以键值对的方式存储到kwargs中
注意:
*和**是必须要写的,否则就变成了普通的形参了- 当我们说不定长参数的时候,就是指
*args和**kwargs
示例:
1 | def test(a, b, *args, **kwargs): |
不定长参数的作用
通过不定长参数,能够实现调用函数时传递实参个数可以随意变换的需求
注意点
- 加了星号
*的变量args会存放所有未命名的变量参数,args为元组 - 而加
**的变量kwargs会存放命名参数,即形如key=value的参数,kwargs为字典 - 一般情况下
*args、**kwargs会在形参的最右侧 args与kwargs的名字可以变,例如叫*aa,**bb都是可以,但一般为了能够让其他的开发者快速读懂我们的代码最好还是不改
特殊情况
缺省参数在*args的后面
1 | def sum_nums_3(a, *args, b=22, c=33, **kwargs): |
说明:
*args后可以有缺省参数,想要给这些缺省参数在调用时传递参数,需要用命名参数传递,否则多余的未命名参数都会给args- 如果有
**kwargs的话,**kwargs必须是最后的
输出结果:
1 | 100 |
8.2 函数返回值拆包
什么是函数返回值拆包
函数返回值拆包:如果一个函数通过return返回了一个元组、列表、集合,可以通过拆包的方式将返回值进行拆分到每个变量中,这就是返回值拆包。
示例:
1 | def test(): |
返回值拆包的作用
通过函数返回值拆包,可以快速的将具体的数据用变量进行存储,这样对数据的处理会更加方便
示例:
1 | def test(): |
拆包的使用
1 | def get_my_info(): |
使用拆包时的注意点
- 拆包时要注意,需要拆的数据的个数要与变量的个数相同,否则程序会异常
8.3 通过星号拆包
通过普通方式拆包
假如有以下函数:
1 | def test(a, b, c): |
现在自己拥有的数据:
1 | nums = [11, 22, 33] |
怎样才能在调用test函数的时候,将nums给传递过去呢?
1 | def test(a, b, c): |
上述代码用的方式虽然能行,但不是很简洁
为了能够用更加简洁的方式实现上述场景需求,Python可以通过*、**将数据拆包后传递
使用*拆包
有时在调用函数时,这个函数需要的是多个参数,而自己拥有的是一个列表或者集合这样的数据,此时就用可以用*拆包
使用方式:
1 | *列表 |
用*拆包的方式实现上述功能:
1 | def test(a, b, c): |
如果为数据元组时使用方式与上述代码一致:
1 | def test(a, b, c): |
集合类型同上:
1 | def test(a, b, c): |
注意:
*对列表、元组、集合可以拆包,但一般都是在调用函数时用
使用**拆包
使用**可以对字典进行拆包,拆包的结果是命名参数
示例:
1 | def test(name, age, address): |
难点
学习不定长参数时,掌握了*args、**kwargs
现在学习拆包时,也用到了*、**
那它们之间有什么关系呢?
答:没有任何关系,只是长得像罢了
示例一:
1 | def test1(*args, **kwargs): |
运行结果:
1 | ----在test2函数中---- |
示例二:
1 | def test1(*args, **kwargs): |
运行结果:
1 | ----在test2函数中---- |
8.4 Python语言中的引用
引入
如下代码中,最后b的值为多少?
1 | a = 1 |
如下代码中,最后b的值为多少?
1 | a = [1, 2] |
什么是引用
引用:就是地址
那地址是什么呢?可以理解为存放数据的空间在内存中的编号
例如:
1 | a = 100 |
怎样知道它的地址呢?
1 | id(a) |
可以直接将上述的结果打印:
1 | print(id(a)) |
运行结果(在不同机器上输出的地址可能不相同):
1 | 4347271232 |
当我们知道了原来引用就是地址之后,再来看如下代码:
1 | a = [1, 2] |
我们可以用id(a)取它的地址:
1 | a = [1, 2] |
接下来定义变量b并且赋值:
1 | a = [1, 2] |
此时输出变量b的引用:
1 | a = [1, 2] |
运行结果(不同机器上的内存地址可能不相同):
1 | 4558971360 |
这说明,此时变量a、b存储的引用都是相同的
由此我们可以得出一个结论:Python中的变量并不是真正存储数据,而是存储的数据所在内存中的地址,我们一般称之为引用
既然变量a、b都指向同一个列表,那么接下来
1 | a.append(3) |
此时变量a、b指向的同一个列表中多了一个数据,即此时列表为[1, 2, 3]
所以a、b此时用print输出相同的结果
补充内容
大家自己试试看a=257, b=257时它们的id还是否会相等。事实上Python 为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。而Python 对小整数的定义是 [-5, 257),只有数字在-5到256之间它们的id才会相等,超过了这个范围就不行了,同样的道理,字符串对象也有一个类似的缓冲池,超过区间范围内自然不会相等了。
总的来说,只有数值型和字符串型,并且在通用对象池中的情况下,a is b才为True,否则当a和b是int,str,tuple,list,dict或set型时,a is b均为False。
赋值运算符=
赋值运算符=,之前为了更好的理解变量,把a=100理解为变量a中存放了100,事实上变量a存储是100的引用
也就是说:在Python中只要用=那么就表示=左边的变量存储了一个新的引用
大白话讲:就是=左边的变量指向了右边的数据
想想下面的代码运行的结果是什么?
1 | a = [1, 2] |
运行结果:
1 | [100, 200, 300] |
而不是:
1 | [1, 2, 3] |
引用当做实参
Python中调用函数时,传递实参实际上都是是引用,即传递的都是地址
只要是传递的引用,那么也就是说在函数中是可以直接对指向的数据进行修改
1 | def test(p): |
运行结果:
1 | 调用test函数之前,nums= [11, 22, 33] |
8.5 函数名也是引用
引入
阅读如下代码,思考会输出什么结果
1 | def test1(): |
运行结果如下:
1 | 我是test1函数哦。。。。 |
你可能会惊讶,为什么第9行调用test1函数输出的是我是test1函数哦。。。。,反而到了第12行再次调用test1函数时变成了我是test2函数哦。。。。
上述问题的原因核心点是:在Python中即使是函数名也是一个变量名,只不过这个变量没有指向普通的数据,而是指向了一段代码;也就是说如果定义了一个函数名字叫做test1就好比是一个变量名test1指向了那个代码块而已,所以当上述代码第11行test1 = test2时,就相当于让test1变量不在指向原本的代码块,而是指向新的代码块即test2指向的代码块,所以当第12行执行test1函数时,会输出我是test2函数哦。。。。
引用的作用
看完上述的引入知识后,相信你会对什么是函数的引入有一个大体的认知了
在此简单总结:所谓函数名当做引用,其实是指在Python中所有的函数名实际上是一个变量名,只不过这个变量名指向的不是常见的数据,而是一段代码,当我们用函数名()是实际上就是让指向的这块代码开始执行,当我们只用函数名时其实就是这个函数的引用
记住:既然函数名也是变量名,那么就可以给它赋值获取它的引用给别的变量
总结:
- 使用
def定义的函数名,实际就是个变量名它存储了函数的引用 - 如果将另外一个变量,例如
b保存了函数的引用,即也指向了同一个函数,那么b()就是调用函数
8.6 匿名函数
什么是匿名函数
没有名字的函数,在Python中用lambda定义
示例:
1 | lambda x, y: x + y # 定义了一个匿名函数 1.没有名字 2.完成2个数的加法操作 |
匿名函数的作用
- 可以用一行代码完成简单的函数定义
- 可以当做实参快速传递到函数中去
使用方式
用lambda关键词能创建匿名函数。这种函数得名于省略了用def声明函数的标准步骤
lambda函数的语法只包含一个语句,如下:
1 | lambda 形参1, 形参2, 形参3: 表达式 |
注意点:lambda函数能接收任何数量的参数但只能返回一个表达式的值,其默认就是返回的,不用写return
既然我们已经知道def定义函数时的变量存储的是函数的引用,所以只要有了这个函数的引用,也就可以通过变量名()的方式调用函数
而函数分为def定义的普通函数,和用lambda定义的匿名函数,所以无论一个变量例如b保存的是普通函数的引用,还是匿名函数的引用,都可以用b()方式调用b指向的函数
一般情况下对匿名函数的使用有2种方式
- 通过
lambda定义匿名函数,然后用一个变量指向这个匿名函数,然后通过变量名()调用这个匿名函数 - 直接在调用其它函数实参的位置通过
lambda定义匿名函数,会将这个匿名函数的引用当做实参进行传递
方式一:
1 | # 定义了一个匿名函数,然后让变量add_2_nums指向它 |
输出结果:
1 | 10+20=30 |
方式二:
1 | def fun(a, b, opt): |
代码案例
想一想,下面的数据如何指定按age或name排序?
1 | stus = [ |
按照name排序:
1 | stus = [ |
按照age排序:
1 | stus = [ |
- 标题: 8.Python函数进阶-2
- 作者: Jinshuo Jiang
- 创建于 : 2025-09-30 21:42:42
- 更新于 : 2025-09-30 21:44:01
- 链接: https://redefine.ohevan.com/2025/09/30/Basics of Python Functions-2/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。