Day 11:【99天精通Python】函数进阶 - *args、**kwargs 与 Lambda 表达式
前言
欢迎来到第11天!
在昨天的课程中,我们学会了定义标准的函数,参数的数量在定义时就是固定的(比如def add(a, b)必须传两个数)。
但在实际开发中,情况往往没那么简单。比如:
- 你想写一个
sum_all()函数,能计算任意数量数字的和(可能是2个,也可能是100个)。 - 你想写一个能够处理各种配置选项的函数,但不想写几十个默认参数。
- 有些简单的函数(比如
x + 1)只用一次,专门写个def显得太繁琐。
今天,我们将解锁Python函数的"完全体"形态,学习动态参数和匿名函数。
本节内容:
*args:接收任意数量的位置参数**kwargs:接收任意数量的关键字参数- 参数的混合使用顺序
- 解包参数(Unpacking)
- Lambda 表达式(匿名函数)
- Lambda 与高阶函数(map, filter, sorted)
一、动态参数:处理不确定数量的输入
1.1 *args:可变位置参数
当你不知道用户会传多少个位置参数时,可以使用*args。
Python会将所有多余的位置参数打包成一个元组(Tuple)。
语法:
deffunc(*args):print(args)注意:
args只是个约定俗成的名字,关键是那个星号*。你写*numbers也可以。
示例:万能求和函数
defsum_all(*numbers):"""计算任意个数字的和"""total=0fornuminnumbers:total+=numreturntotalprint(sum_all(1,2))# 3print(sum_all(1,2,3,4))# 10print(sum_all())# 01.2 **kwargs:可变关键字参数
如果你想接收任意数量的key=value形式的参数,可以使用**kwargs。
Python会将它们打包成一个字典(Dictionary)。
语法:
deffunc(**kwargs):print(kwargs)注意:
kwargs代表 “keyword arguments”,关键是双星号**。
示例:用户信息记录
defprint_info(name,**details):print(f"Name:{name}")forkey,valueindetails.items():print(f"{key}:{value}")print_info("Alice",age=25,city="New York")# 输出:# Name: Alice# age: 25# city: New Yorkprint_info("Bob",job="Engineer",status="Single",level=99)二、参数的组合与解包
2.1 这里的顺序很重要!
如果一个函数同时包含了多种类型的参数,定义的顺序必须是:
- 位置参数(常规参数)
*args(可变位置参数)- 默认参数(关键字参数)
**kwargs(可变关键字参数)
defmixed_func(a,b,*args,**kwargs):print(f"a={a}, b={b}")print(f"args={args}")print(f"kwargs={kwargs}")mixed_func(1,2,3,4,5,x=10,y=20)# 输出:# a=1, b=2# args=(3, 4, 5)# kwargs={'x': 10, 'y': 20}2.2 参数解包:把列表"炸"开传进去
刚才我们是用*来收集参数(打包),反过来,我们在调用函数时,也可以用*来解散列表(解包)。
defadd(a,b,c):returna+b+c nums=[1,2,3]# 错误调用:add(nums) -> 报错,因为只传了1个参数(整个列表)# 正确调用(解包)print(add(*nums))# 等同于 add(1, 2, 3) -> 6同理,**可以用来解包字典:
defintroduce(name,age):print(f"我是{name},今年{age}岁")info={"name":"小明","age":18}# introduce(info) -> 报错introduce(**info)# 等同于 introduce(name="小明", age=18)三、Lambda 表达式:一行代码的函数
3.1 什么是 Lambda?
Lambda 表达式(也称匿名函数)是一种定义简单函数的快捷方式。它没有名字,通常只包含一行代码。
语法:
lambda参数:表达式- 关键字
lambda开头。 - 参数可以有多个,用逗号分隔。
- 冒号后是表达式,自动返回该表达式的计算结果(不需要
return)。
对比:
# 普通函数defsquare(x):returnx*x# Lambda 函数square_lambda=lambdax:x*xprint(square(5))# 25print(square_lambda(5))# 253.2 什么时候用 Lambda?
Lambda 通常不单独使用(单独用不如直接 def),它主要是作为参数传递给其他函数(如高阶函数)。
场景 1:配合 sort() 排序
比如我们有一个字典列表,想根据字典里的某个键排序。
students=[{"name":"Alice","age":25},{"name":"Bob","age":20},{"name":"Charlie","age":22}]# 按年龄排序# sort(key=函数) -> 这里需要一个函数,告诉sort按什么排students.sort(key=lambdas:s["age"])print(students)# [{'name': 'Bob', 'age': 20}, {'name': 'Charlie', 'age': 22}, {'name': 'Alice', 'age': 25}]场景 2:配合 map() 映射
map(func, iterable):对序列中的每个元素应用 func。
nums=[1,2,3,4,5]# 将所有数字平方squared=list(map(lambdax:x**2,nums))print(squared)# [1, 4, 9, 16, 25]场景 3:配合 filter() 过滤
filter(func, iterable):保留 func 返回 True 的元素。
nums=[1,2,3,4,5,6]# 筛选偶数evens=list(filter(lambdax:x%2==0,nums))print(evens)# [2, 4, 6]四、实战练习
练习1:超级连接器
编写一个函数concat_str(*args, sep="/"),它可以接收任意多个字符串,并用指定的分隔符(默认是/)连接它们。
defconcat_str(*args,sep="/"):returnsep.join(args)print(concat_str("home","user","docs"))# home/user/docsprint(concat_str("2026","01","11",sep="-"))# 2026-01-11练习2:自定义排序逻辑
有一个元组列表[(1, 'one'), (3, 'three'), (2, 'two')],请使用sorted()和lambda表达式,按照元组的第二个元素(字符串)的长度进行排序。
data=[(1,'one'),(3,'three'),(2,'two')]# 按字符串长度排序 ('one':3, 'three':5, 'two':3)# 注意:one和two长度一样,保持原序sorted_data=sorted(data,key=lambdax:len(x[1]))print(sorted_data)# [(1, 'one'), (2, 'two'), (3, 'three')]五、常见问题
Q1:*args和**kwargs必须这么写吗?
不是强制的。你完全可以写*vars和**options。但是,*args和**kwargs是Python社区的硬性约定,为了让别人能看懂你的代码,请务必遵守这个命名。
Q2:Lambda 能包含复杂的逻辑吗?
不能。Lambda 只能包含一个表达式。它不能包含赋值语句(x=1)、循环(for)、流程控制(if...elif,但可以用三元运算符x if c else y)。如果逻辑复杂,请老老实实写def。
Q3:args是列表吗?kwargs是字典吗?
args本质上是一个元组 (tuple)(不可变)。kwargs本质上是一个字典 (dict)。
六、小结
关键要点:
- 想让函数接收任意个参数?用
*args和**kwargs。 *args把多余参数打包成元组,**kwargs打包成字典。lambda适合写那种"用完即扔"的短小函数,特别是在排序和数据处理时。- 参数解包
func(*list)是非常实用的技巧。
七、课后作业
- 最大值函数:不要使用内置的
max(),编写一个接受任意数量数字的函数my_max(*args)并返回最大值。 - 字典构造器:编写一个函数
make_dict(**kwargs),它接收任意关键字参数,并返回一个字典,但要求所有的key都必须转换为大写。- 输入:
make_dict(a=1, b=2) - 输出:
{'A': 1, 'B': 2}
- 输入:
- 复杂排序:有一个学生列表
students = [{'name':'Tom','score':80}, {'name':'Jerry','score':90}, {'name':'Spike','score':80}]。请按照分数降序排序;如果分数相同,则按名字字母序升序排序。(提示:元组比较规则(score, name))。
下节预告
Day 12:文件操作 (File I/O)- 写了这么多代码,结果一关机数据就没了?明天我们学习如何读写文件,把数据持久化保存下来!
系列导航:
- 上一篇:Day 10 - 函数基础
- 下一篇:Day 12 - 文件操作(待更新)