很多时候,我们的函数需要参数,有多种情况的参数
- 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
所以*字典
和 **字典
的顺序是 :*字典
在前,**字典
在后。
那么得出结论,各自之间的形参位置是:普通参数=*字典 ---> **字典 --> 位置参数
而且当使用 **字典 解包后,函数的形参一定要和字典的键同名,否则解包失败