python *args,和 **kwargs的使用,* 与 ** 解包使用

本文深入解析Python函数参数的种类,包括普通形参、缺省参数、不定长参数(*args和**kwargs)及其使用方法,通过实例展示如何正确传递和接收各种类型的参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很多时候,我们的函数需要参数,有多种情况的参数

  • def func1(x,y): —普通形参
  • def func2(x,y=1): —普通形参 和 位置参数,也叫缺省参数,有默认值
  • def func3(x,y,z,…): — 不定长参数
  • def func4(x=?,y=?,z=?,…) —不定长位置参数 ,参数键值由传入的实参决定
  • def func4(x,y=1,z,a,b,?=?) —前面所有情况混合

那么关于不定长参数,普通形参已经不可以满足了,这时候,*args 和 **kwargs,就可以解决问题。

def func1(x,*args,**kwargs):
	print(x)
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

func1(100,2,'a',('x','y'), name='jack',age=20)

>>> 100
	(2, 'a', ('x', 'y'))
	{'name': 'jack', 'age': 20}

可以看出,除去普通参数 ‘x’对应的 ‘100’, 后面的 2, ‘a’, ('x','y'), 都被 print(args)给打印出来了, 由此可见,*args元祖的形式接收所有的不定长的参数,不管是什么类型,都一概收入囊中。
而到了 name='jack',age=20 这里,*args已经不能接收了,因为不知道 name 和 name的值 jack要以怎样的形式安放,这时候**kwargs就体现了其特殊作用了。**kwargs``以字典的形式 接收所有位置参数,然后将等号左边作为字典的键,等号右边作为字典的值,这样通过打印,就得到了 {'name': 'jack', 'age': 20}, 而其长度也是不定长的。有多少接收多少。

那么思考一下,如果我的实参是一个字典,直接传入会怎样?

def func1(*args,**kwargs):
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

dict_1 = {'a':1,'b':2}
func1(dict_1)

>>> ({'a': 1, 'b': 2}, )
	{}

再一次印证了 *args元祖的形式接收所有的不定长的参数,即使是字典,都一概收入囊中。

大家肯定在很多时候会看到,一个元祖前面有*,或者一个字典前面有两颗**
现在就用几个例子看看

def func1(*args,**kwargs):
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

str_1 = 'hello'
func1(*str_1)

>>> ('h', 'e', 'l', 'l', 'o')   //字符串被分解成了一个个字符,并以字符串的形式放进了元祖里面
	{}
def func1(*args,**kwargs):
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

tuple_1 = (100,200,300)
func1(tuple_1)
func1(*tuple_1)

>>> ((100, 200, 300),) //没有用 *, 就将 tuple_1 整个的放进了元祖中
	(100, 200, 300)   //元祖里面的元素被一个个解包 放进了元祖中
	{}
def func1(*args,**kwargs):
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

dict_1 = {'name': 'jack', 'age': 20}
func1(*dict_1)

>>> ('name', 'age')  //字典的键通过*解包放进了元祖里面
	{}
def func1(*args,**kwargs):
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

dict_1 = {'name': 'jack', 'age': 20}
func1(*dict_1)

>>> ('name', 'age')  //字典的键通过*解包放进了元祖里面
	{}
def func1(*args,**kwargs):
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

int_1 = 100
func1(*int_1)

>>> TypeError: func1() argument after * must be an iterable, not int //*后面需要跟可迭代对象,即可遍历的。显然int类型是不可变的。

所以我们看出,*args 可以将 * 解包后的元素放进元祖里,如果没有解包,那么是将整个当成一个元素放进元祖中,
* 可解包的必须是可迭代对象,字符串元祖字典等都可以, 整型类的不可以。

那么下面我们看看两颗星的,**

def func1(*args,**kwargs):
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

tuple_1 = (100,200,300)
func1(**tuple_1)

>>> TypeError: func1() argument after ** must be a mapping, not tuple

显然 ** 后面不能够加 元祖,必须要是一个mapping 对象,通过测试,其他类型也不行,但是字典可以。
我之前认为,*是解包,**是解包两次,如果元祖中再套一个元祖是否可行?
事实是不行的

def func1(*args,**kwargs):
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

tuple_1 = ((100,200,300), )
func1(**tuple_1)

>>> TypeError: func1() argument after ** must be a mapping, not tuple

那么再来看看 **字典

def func1(*args,**kwargs):
	print(args)   //在函数中使用时需要将*去掉
	print(kwargs)

dict_1 = {'name': 'jack', 'age': 20}
func1(**dict_1)

>>> ()  
	{'name': 'jack', 'age': 20}  

字典倒是可以 通过**解包,但是这好像和传进去一个字典没啥区别,我为啥要用 **kwags 接收参数呢?
所以我们不再用 **kwags 接收 ** 解包后的参数
那么再来看一个例子

def func1(name,age):
	print(name)   
	print(age)
	
def func2(age,name):
	print(age)   
	print(name)
dict_1 = {'name': 'jack', 'age': 20}
func1(**dict_1)
func2(**dict_1)

>>> jack 
	20 
>>> 20
	jack 

原来解包后的字典可以按 键的同名实形参取到对应的值,而且形参的位置也没有规定,都会取到键对应的值。
但是有一个问题,如果形参变量名与实参字典的键不同名呢?

def func1(a,b):
	print(a)   
	print(b)
	
dict_1 = {'name': 'jack', 'age': 20}
func1(**dict_1)

>>>  TypeError: func2() got an unexpected keyword argument 'name'

所以得出结论,使用 **字典 解包,当做函数参数传入,函数的形参必须与字典的键同名,否则会报错。

那我们再思考一下,*解包字典,可以拿到其对应的键,是不是也可以当做函数参数传入呢?是否也需要相同变量名呢?

def func1(a,b):
	print(a)   
	print(b)
	
dict_1 = {'name': 'jack', 'age': 20}
func1(*dict_1)

>>>  name
	 age

我们可以看到,使用 *字典 可以将键传入函数,而函数形参可以任意名字。

最后我们考虑一下, 当普通参数、位置参数与 *字典**字典 参数之间,是否顺序可以随意

def func1(a,b,c,x=1):
	print(a)   
	print(b)
	print(c)
	print(x)
	
dict_1 = {'name': 'jack', 'age': 20}
func1(*dict_1, 8)
func1(8, *dict_1)

>>>  name
	 age
	 8
	 1
>>> 8
	name
	age
	1

所以 *字典和普通参数之间,不需要考虑顺序和命明,但是需要注意位置参数x=1无论怎样都是放在最后的,如果放前面pycharm也会冒红报错。
下面看 **字典 参数顺序

def func1(u,age,name):
    print(name)
    print(age)
    print(u)
	
dict_1 = {'name': 'jack', 'age': 20}
func1(8, **dict_1)

>>>  name
	 age
	 8
def func1(age,name,u):
    print(name)
    print(age)
    print(u)
	
dict_1 = {'name': 'jack', 'age': 20}
func1(**dict_1, 8)

>>>  SyntaxError: positional argument follows keyword argument unpacking

看来 **字典 参数顺序是需要放在普通参数的后面。但是如果有位置参数,位置参数也还是一样放在最后面的。
那么看看 *字典**字典 的顺序

def func1(a,b,age,name):
    print(name)
    print(age)
    print(a)
    print(b)
	
dict_1 = {'name': 'jack', 'age': 20}
func1(*dict_1, **dict_1)

>>> jack
	20
	name
	age
def func1(age,name,a,b):
    print(name)
    print(age)
    print(a)
    print(b)

	
dict_1 = {'name': 'jack', 'age': 20}
func1(**dict_1, *dict_1)

>>> SyntaxError: iterable argument unpacking follows keyword argument unpacking

所以*字典**字典的顺序是 :*字典在前,**字典在后。

那么得出结论,各自之间的形参位置是:普通参数=*字典 ---> **字典 --> 位置参数
而且当使用 **字典 解包后,函数的形参一定要和字典的键同名,否则解包失败

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值