位置参数
顾名思义,在调用函数时根据函数定义的参数位置来传递参数
def fun(a,b):
print("arg a:", a)
print("arg b:", b)
fun(10,20)
# arg a: 10
# arg b: 20
在使用位置参数进行参数传递时,要保证参数数目和顺序对应,不然会报错。
关键字参数
在调用函数时,也可以通过 “键-值” 对来对应赋值,这样不用参数间的顺序问题,同时程序也更明了。
def fun(a,b):
print("arg a:", a)
print("arg b:", b)
fun(a = 10,b = 20)
# arg a: 10
# arg b: 20
fun(b = 10,a = 20)
# arg a: 20
# arg b: 10
fun(10,b = 20)
# arg a: 10
# arg b: 20
fun(10,a = 20)
# TypeError: fun() got multiple values for argument 'a'
fun(a = 10,20)
# SyntaxError: positional argument follows keyword argument
根据上面程序提示,可以得知:
根据关键字赋值时不区分先后顺序。
在单个函数调用时,不能同时给单个参数赋多个值。
位置参数和关键字参数同时存在时,位置参数应该在关键字参数之前。
默认参数
默认参数是在函数定义的时候已经给某些参数赋值,即为默认值。默认赋值的参数即为默认参数,在对应参数有参数传递时要重新赋值,没有参数传递时即为默认值。
def fun(a,b,c = 30):
print("arg a:", a)
print("arg b:", b)
print("arg c:", c)
fun(10,20)
# arg a: 10
# arg b: 20
# arg c: 30
fun(10,20,40)
# arg a: 10
# arg b: 20
# arg c: 40
fun(10,b = 20,c = 40)
# arg a: 10
# arg b: 20
# arg c: 40
fun(a = 10,b = 20,c = 40)
# arg a: 10
# arg b: 20
# arg c: 40
fun(b = 10,c = 20,a = 40)
# arg a: 40
# arg b: 10
# arg c: 20
def fun(a,c = 30,b):
print("arg a:", a)
print("arg b:", b)
print("arg c:", c)
# SyntaxError: non-default argument follows default argument
可知,不管是在函数定义或者函数调用中,用 = 赋值的参数都应该在不用 = 赋值的参数之后,即位置参数在关键字参数之前。
可变长度参数
在实际编程中,有时我们希望给函数传递不同数量的参数,或者在函数定义中直接设置可变长度的参数,这就是可变长度参数。变长参数在函数中的声明不是显式命名的,因为参数数目在运行之前是未知的,参数的个数取决于运行时实际传递的参数个数。
可变长度参数主要是靠包裹(packing)位置参数或者包裹关键字参数实现的。
非关键字可变长参数(元组)
当函数调用的时候,所有的形参(必须的和默认的)都将值赋给了在函数声明中相对应的局部变量,剩下的非关键字参数可以按照顺序插入到一个元组中便于访问。
def fun(a,*args): print("arg a:", a) for i in args: print("*args:", i) fun(10,20,30,40) # arg a: 10 # *args: 20 # *args: 30 # *args: 40 fun(a = 10,20,30,40) # SyntaxError: positional argument follows keyword argument fun(20,30,40,a = 10) # TypeError: fun() got multiple values for argument 'a'
同样的,在参数调用也需要保证,位置参数在关键字参数之前,不能重复赋值。
* 操作符之后的形参将作为元组传递给函数,元组保存了参数 arg 之后所有传递给函数的参数。如果没有,则为空。另外 * 后边只是表示元组的名称,并不一定要用 args 来表示,任何一个变量都行。只是 * 是必需的。
关键字变量参数(字典)
同样对于含有关键字的参数来说,可以用变量参数字典来处理这种情况。
def fun(a,**kwargs): print("arg a:", a) for key, value in kwargs.items(): print("**kwargs:", key, "=", value) fun(10,b = 20,c = 30,d = 40) # arg a: 10 # **kwargs: b = 20 # **kwargs: c = 30 # **kwargs: d = 40 fun(b = 20,c = 30,d = 40,a = 10) # arg a: 10 # **kwargs: b = 20 # **kwargs: c = 30 # **kwargs: d = 40 fun(b = 20,c = 30,d = 40,10) # SyntaxError: positional argument follows keyword argument
上面的程序说明,在保证位置参数有变量输入的情况下,**kwargs 会把剩下的关键字参数组合成字典,而不关心关键字参数的顺序。但仍要保证位置参数在关键字参数之前。
为了区分关键字参数和非关键字参数,在关键字参数前会有 ** 作为区别。同样 ** 后边只是表示字典的名称,并不一定要用 kwargs 来表示,而是任何一个都可以。只是 ** 是必需的。
解包裹参数
上面我们利用 *(元组)和 **(字典)实现了函数定义,实际上,在函数定义的时候,也可以使用 * 和 **,这种操作叫做解包裹(unpacking)。
* 解包裹
def fun(a,b,c): print("arg a:", a) print("arg b:", b) print("arg c:", c) arg = (10,20) fun(30,*arg) # arg a: 30 # arg b: 10 # arg c: 20 fun(*arg,30) # arg a: 10 # arg b: 20 # arg c: 30
元组解包时,就地展开,元组中的一个元素对应一个位置参数。因此在使用过程中要注意赋值的顺序,一一对应。
** 解包裹
def fun(a,b,c): print("arg a:", a) print("arg b:", b) print("arg c:", c) kwargs = {'a':10,'b':20} fun(c = 30,**kwargs) # arg a: 10 # arg b: 20 # arg c: 30 fun(**kwargs,30) # SyntaxError: positional argument follows keyword argument unpacking
字典解包时,就地展开,如果都是关键字参数,便不用在意顺序。
由于字典解包裹时,相当于关键字参数赋值,因此位置参数不能放置在 **kwargs 后边。
大乱斗——位置参数、默认参数、可变参数的混合
使用时的基本原则是:位置参数、默认参数、* 包裹、** 包裹按顺序排列(定义和调用都保证这个顺序)
def fun(a, b, c=30, d=40, *args, **kwargs):
print("arg a:", a)
print("arg b:", b)
print("arg c:", c)
print("arg d:", d)
for i in args:
print("*args:", i)
for key, value in kwargs.items():
print("**kwargs:", key, ",", value)
fun(10,20,50,60,70,80,90,e = 100,f = 110,g = 120)
# arg a: 10
# arg b: 20
# arg c: 50
# arg d: 60
# *args: 70
# *args: 80
# *args: 90
# **kwargs: e , 100
# **kwargs: f , 110
# **kwargs: g , 120
从上面可以看出,*args 包裹只“收留”那些不带有关键字的参数,组成元组。而 **kwargs 只“收留”那些带有关键字的参数,组成字典。并且根据位置参数必须要在关键字参数之前的规则来看,*args 必须要在 **kwargs 之前
再看一个混合使用的例子:
def fun(a, b, *args, c=30, d=40, **kwargs):
print("arg a:", a)
print("arg b:", b)
print("arg c:", c)
print("arg d:", d)
for i in args:
print("*args:", i)
for key, value in kwargs.items():
print("**kwargs:", key, ",", value)
fun(10,20,50,60,70,80,90,e = 100,f = 110,g = 120)
# arg a: 10
# arg b: 20
# arg c: 30
# arg d: 40
# *args: 50
# *args: 60
# *args: 70
# *args: 80
# *args: 90
# **kwargs: e , 100
# **kwargs: f , 110
# **kwargs: g , 120
从上边可以看到,*args 可以位于 a,b 之后,这时因为*args 都是非关键字参数,而 a,b 也是非关键字参数,*args 之后是默认参数和关键字参数,符合之前强调的规则,因此正确运行。
假设把 *args 放在 a 和 b 之间,Python 便不能识别哪一部分是可变长度参数,哪一部分是位置参数,便会报错。
def fun(a, *args, b, c=30, d=40, **kwargs):
print("arg a:", a)
print("arg b:", b)
print("arg c:", c)
print("arg d:", d)
for i in args:
print("*args:", i)
for key, value in kwargs.items():
print("**kwargs:", key, ",", value)
fun(10,20,50,60,70,80,90,e = 100,f = 110,g = 120)
# TypeError: fun() missing 1 required keyword-only argument: 'b'
但是上面的程序如果我们想要给 c 和 d 赋值,又该如何处理,可以用关键字参数对 c 和 d 进行赋值或者将默认参数放置到 *args 前边进行赋值。
用关键字进行赋值
def fun(a, b, *args, c=30, d=40, **kwargs): print("arg a:", a) print("arg b:", b) print("arg c:", c) print("arg d:", d) for i in args: print("*args:", i) for key, value in kwargs.items(): print("**kwargs:", key, ",", value) fun(10,20,50,60,70,c = 80,d = 90,e = 100,f = 110,g = 120) # arg a: 10 # arg b: 20 # arg c: 80 # arg d: 90 # *args: 50 # *args: 60 # *args: 70 # **kwargs: e , 100 # **kwargs: f , 110 # **kwargs: g , 120
改变函数定义时参数的位置顺序
def fun(a, b, c=30, d=40, *args, **kwargs): print("arg a:", a) print("arg b:", b) print("arg c:", c) print("arg d:", d) for i in args: print("*args:", i) for key, value in kwargs.items(): print("**kwargs:", key, ",", value) fun(10,20,50,60,70,e = 100,f = 110,g = 120) # arg a: 10 # arg b: 20 # arg c: 50 # arg d: 60 # *args: 70 # **kwargs: e , 100 # **kwargs: f , 110 # **kwargs: g , 120
再看一种情况:
def fun(a, b, *args, c=30):
print("arg a:", a)
print("arg b:", b)
print("arg c:", c)
for i in args:
print("*args:", i)
fun(10,20,50,60,70,c =40)
# arg a: 10
# arg b: 20
# arg c: 40
# *args: 50
# *args: 60
# *args: 70
fun(10,20,50,60,70,40)
# arg a: 10
# arg b: 20
# arg c: 30
# *args: 50
# *args: 60
# *args: 70
# *args: 40
当默认参数变为最后时,是否使用关键字赋值结果是完全不同的,应该注意。
最后
位置参数要保证数目顺序一一对应
默认参数重新赋值时要注意参数位置
关键字参数一定在非关键字参数之后,不管是调用阶段还是定义阶段
关键字参数的顺序没有要求,保证数目对应即可。
使用可变长度参数时,要注意各个参数之间的排列顺序。