一篇文章彻底搞懂Python魔法参数args和kwargs(通俗易懂)!
一篇文章彻底搞懂Python魔法参数args和kwargs(通俗易懂)!
提前划重点
- 什么是位置参数和关键字参数?
- *args本质是个元组
- **kwargs本质是个字典
- 如何结合使用*args和**kwargs
- 在装饰器中使用 * args 和 **kwargs
位置参数和关键字参数
在深入了解 * args 和 **kwargs 之前,咱们得先熟悉一下 Python 函数参数中的两个 “常规军”—— 位置参数和关键字参数,它们可是函数传参的基础。
位置参数,顾名思义,就是在调用函数时,按照参数的顺序依次传递给函数的参数,实参的位置和形参的位置要一一对应,就像排队领东西,先来的先领,顺序不能乱。比如说,咱们定义一个计算两数之和的函数:
1
2
|
def add_numbers(a, b): return a + b |
这里的 a 和 b 就是位置参数,当我们调用这个函数时,就得按照顺序传入两个数:
1
2
|
result = add_numbers( 3 , 5 ) print (result) |
在这个例子中, 3 会被赋值给 a, 5 会被赋值给 b,函数返回它们的和 8。
而关键字参数呢,就灵活多了。它是指在调用函数时,通过指定参数名来为参数赋值,这样就不用拘泥于参数的位置顺序,让代码更加清晰易读。还是用刚才的加法函数举例,我们可以这样调用:
1
|
result = add_numbers(a = 3 , b = 5 ) |
甚至还可以交换顺序:
1
|
result = add_numbers(b = 5 , a = 3 ) |
不管顺序如何,只要参数名写对,就能准确地把值赋给对应的形参,是不是很方便?这在函数参数较多的时候特别有用,能让你一眼就看明白每个参数的含义。
另外,位置参数和关键字参数还能混合使用,不过要记住一条规则:位置参数必须在关键字参数之前。比如:
1
2
3
4
|
def greet(name, message): print (f "{message}, {name}!" ) greet( "小明" , message = "你好" ) |
这样写没问题,但要是写成 greet(message=“你好”, “小明”),Python 就会报错,因为违反了位置参数在前的原则。
*args 本质是个元组
*args 就像是一个 “参数收集器”,它能够把函数调用时多余的位置参数一股脑儿地收集起来,然后打包成一个元组(tuple)。这里要注意哦,名字不一定非得是args,写成numbers、values 之类的都没问题,关键是那个星号,它就像是一个 “魔法标记”,告诉 Python:“嘿,我要收集多余的位置参数啦!”
咱们来看个例子,假如你要写一个函数,计算任意多个数字的总和,你不确定用户会输入几个数字,这时候 * args 就能大显身手了:
1
2
3
4
5
|
def sum_numbers( * args): total = 0 for number in args: total + = number return total |
在这个函数里,*args 就像一个口袋,不管你传入多少个位置参数,它都能接住,然后把这些参数变成一个元组。比如,咱们这样调用这个函数:
1
2
|
result = sum_numbers( 1 , 2 , 3 , 4 , 5 ) print (result) |
这里传入的 1、2、3、4、5 就会被 * args 收集起来,变成元组 (1, 2, 3, 4, 5),然后在函数内部,通过循环遍历这个元组,把每个数字相加,最终得到总和 15。
**kwargs 本质是个字典
讲完了*args,咱们再来看看另一位主角 ——**kwargs。它同样有个神奇的 “魔法标记”—— 两个星号**,有了它,函数就能把调用时传入的任意数量的关键字参数统统收集起来,整理成一个字典(dict)。和 * args 一样,名字不固定,写成 **params、**options 之类的都行,关键是那两个星号。
咱们来看个例子,假如你要写一个函数,用来展示一个人的详细信息,你不知道用户会提供哪些具体信息,这时候 **kwargs 就能派上用场啦:
1
2
3
|
def show_person_info( * * kwargs): for key, value in kwargs.items(): print (f "{key}: {value}" ) |
在这个函数里,**kwargs 就像一个万能的信息收纳盒,不管你传入多少个关键字参数,它都能接住,然后把这些参数变成一个字典。比如,咱们这样调用这个函数:
1
|
show_person_info(name = "小明" , age = 20 , city = "北京" ) |
这里传入的 name=“小明”、age=20、city=“北京” 就会被 **kwargs 收集起来,变成字典 {“name”: “小明”, “age”: 20, “city”: “北京”},然后在函数内部,通过遍历这个字典,把每个键值对都打印出来,展示出这个人的详细信息。
从函数定义的角度来看,**kwargs 也必须放在参数列表的最后面,这是为了让 Python 解释器能正确地识别参数。要是你把它放在前面,后面的参数就可能会被误认成关键字参数,导致程序出错。
如何结合使用 * args 和 **kwargs
了解了 * args 和 **kwargs 各自的特点,接下来咱们看看怎么把它们结合起来使用.
在实际编程中,有很多场景需要同时处理位置参数和关键字参数,而且数量还不确定。比如说,你要写一个函数来记录日志信息,日志可能包含一些固定的格式信息(类似关键字参数),还可能有一些额外的描述信息(类似位置参数),这时候 * args 和 **kwargs 就能完美配合。
咱们来看个例子:
1
2
3
4
5
6
|
def log_message(message_type, * args, * * kwargs): print (f "[{message_type}]" ) for arg in args: print (arg) for key, value in kwargs.items(): print (f "{key}: {value}" ) |
在这个 log_message 函数中, message_type 是一个固定的位置参数,用来指定日志的类型,比如 “ERROR”、“INFO” 之类的。而 *args 用来接收一些额外的描述信息,**kwargs 则用来接收一些带有特定名称的详细信息,像日志发生的时间、代码行数等。
咱们这样调用这个函数:
1
|
log_message( "INFO" , "程序启动成功" , time = "2023-09-15 10:00:00" , line_number = 100 ) |
这里,“程序启动成功” 会被args 收集,变成元组 (“程序启动成功”,),而 time=“2023-09-15 10:00:00” 和 line_number=100 会被**kwargs 收集,变成字典 {“time”: “2023-09-15 10:00:00”, “line_number”: 100}。函数内部先打印日志类型 “INFO”,接着遍历*args 打印额外描述,再遍历 **kwargs 打印详细信息,输出就会像这样:
1
2
3
4
|
[INFO] 程序启动成功 time: 2023 - 09 - 15 10 : 00 : 00 line_number: 100 |
在装饰器中使用 * args 和 **kwargs
聊完了 * args 和 **kwargs 的基本用法,咱再来看看它们在装饰器里如何使用!
对于装饰器不太熟悉的小伙伴,可以看一下Up上期的文章哦!
那*args 和 **kwargs 在装饰器里起什么作用呢?想象一下,如果被装饰的函数可能接收不定数量的参数,就像咱们之前讲的*args 和**kwargs 的用法,这时候装饰器里的内部函数就得用 *args 和**kwargs 来接收这些参数,然后原封不动地传给原函数,否则参数就传递不进去,会导致报错。
咱们来看个例子,假设你要写一个装饰器,用来记录函数的执行时间,不管这个函数接收几个参数,都能正常工作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import time def timeit(func): def wrapper( * args, * * kwargs): start_time = time.time() result = func( * args, * * kwargs) end_time = time.time() print (f "{func.__name__} 函数执行时间: {end_time - start_time} 秒" ) return result return wrapper @timeit def add_numbers( * args): return sum (args) @timeit def show_info( * * kwargs): for key, value in kwargs.items(): print (f "{key}: {value}" ) |
在这个例子中, timeit 是装饰器函数,它的内部函数 wrapper 使用 * args 和 **kwargs 接收任意数量的参数,然后把这些参数传给原函数 func,不管是 add_numbers 函数接收不定数量的位置参数,还是 show_info 函数接收不定数量的关键字参数,都能被 wrapper 函数正确接收并传递,同时还能记录下函数的执行时间。比如,咱们调用 add_numbers(1, 2, 3, 4, 5),会先记录开始时间,执行 add_numbers 函数计算总和,再记录结束时间,打印出函数执行时间,最后返回总和;调用 show_info(name=“小明”, age=20) 也是类似,先记录时间,打印信息,再返回 None(因为 show_info 函数没有返回值)。
要是装饰器里的内部函数不使用 * args 和 *kwargs,而是写死了固定的参数,那这个装饰器就只能用于特定参数数量和类型的函数,灵活性就大打折扣了。比如说,你把 wrapper 函数写成 def wrapper(a, b),那它就只能装饰接收两个位置参数的函数,要是用来装饰 add_numbers(args) 这种不定参数的函数,就会报错,提示参数数量不匹配。
所以说,args 和 **kwargs 在装饰器里就像是两座 “桥梁”,让装饰器能够无缝对接各种不同参数的函数,极大地拓展了装饰器的应用范围,让你的代码复用性更强,不管是简单的函数增强,还是复杂的框架开发,都离不开它们的身影,掌握了在装饰器中使用args 和 **kwargs 的技巧,你就能在 Python 编程的世界里更加游刃有余啦!
总结
到此这篇关于Python魔法参数args和kwargs的文章就介绍到这了。
学习资料见知识星球。
以上就是今天要分享的技巧,你学会了吗?若有什么问题,欢迎在下方留言。
快来试试吧,小琥 my21ke007。获取 1000个免费 Excel模板福利!
更多技巧, www.excelbook.cn
欢迎 加入 零售创新 知识星球,知识星球主要以数据分析、报告分享、数据工具讨论为主;
1、价值上万元的专业的PPT报告模板。
2、专业案例分析和解读笔记。
3、实用的Excel、Word、PPT技巧。
4、VIP讨论群,共享资源。
5、优惠的会员商品。
6、一次付费只需129元,即可下载本站文章涉及的文件和软件。
共有 0 条评论