(1)python函数传参方式
位置传递,根据位置顺序对变量赋值,对应参数叫位置参数或非关键字参数(Positional Arguments);
关键字传递,根据关键字对变量赋值,对应参数叫关键字参数(Keyword Arguments)。
“注意:如果定义函数时,参数列表中没有“单个星号”(包括独立一个星号,或带一个星号的变量),则通常是位置参数不带=,而关键字参数带=;但如果参数列表没有“单个星号”(包括独立一个星号,或带一个星号的变量),则星号前面的都是位置参数(无论是否带等号),而星号后面的都是关键字参数(无论是否带等号。)
下例是‘位置传参’,按位置顺序自动匹配a=1,b=2,c=3,d=4。
#基于位置传参
def func(a, b, c, d):
print(a, b, c, d)
func(1,2,3,4)
1 2 3 4
下例是‘关键字传参’,使用函数时,根据关键字进行匹配,与位置无关。
def func(a=1, b=2, c=3, d=4):
print(a, b, c, d)
func(b=1,c=2,d=3,a=4)
4 1 2 3
注意:定义函数时,位置参数和关键字参数可以混用,但所有位置参数都必须在关键字参数之前,否则出错:
def func(a, b, c=3, d=4): #正确
print(a, b, c, d)
def func(a=1, b, c, d): #错误
print(a, b, c, d)
def func(a, b, c=3, d): #错误
print(a, b, c, d)
注意:调用函数时,位置参数可以通过关键字方式传递,关键字参数也可以通过位置方式传递,但位置参数都必须在关键字参数前面:
def func(a, b, c, d):
print(a, b, c, d)
func(a=1,b=2,c=3,d=4) #正确
func(1,2,c=3,d=4) #正确
func(a=1,2,c=3,4) #错误
def func(a=1, b=2, c=3, d=4):
print(a, b, c, d)
func(1,2,3,4) #正确
func(1,2,c=3,d=4) #正确
func(a=1,2,3,4) #错误
(2)理解 * 和 **
单星号*在列表[ ]、元组( )、字典{ }前表示解包,即将内部元素单独取出:
print(*[1,2,3])
print(*(1,2,3))
print(*{'a':1,'b':2,'c':3}) #注意单星号对于字典,只取字典的健
1 2 3
1 2 3
a b c
############################################################
def func(a,b,c):
print(a,b,c)
d=[1,2,3]
func(*d)
1 2 3
双星号**在字典{ }前表示将字典的健和值形成“健=值”对,但只能在函数中体现:
def func(a=1,b=2,c=3):
print(a,b,c)
d={'a':2,'b':3,'c':4}
func(**d)
2 3 4
(3) *args 和 **kwargs
定义函数时经常看到*args 和 **kwargs,他们分别表示接收任意多个非关键字参数(位置参数)和关键字参数,其中args和kwargs只是一个代号,可以替换成任意其他字符,起决定作用的是*和**,不可变。
定义可变参数的目的是为了简化调用,*和**的作用:打包参数。
单星号变量*args代表这个位置接收任意多个非关键字参数,并将其转化成元组args;
双星号变量**kwargs代表这个位置接收任意多个关键字参数,并将其转化成字典kwargs。
def func(*args):
print(args)
print(type(args))
for i in args:
print(i)
func(1,2,3)
#输出
(1, 2, 3)
1
2
3
上例表示函数接收任意多个非关键字参数,然后在执行函数时将输入的非关键字参数打包成“元组”args。
def func(*args):
print(args)
print(type(args))
b = [1,2,3]
func(*b)
#输出
(1, 2, 3)
上例同样表示函数接收任意多个非关键字参数,不同的是执行函数时用了*b,他表示先将[1,2,3]解包成1,2,3,即成为非关键字参数,然后将他们打包成“元组”args。
def func(**kwargs):
print(kwargs)
print(type(kwargs))
for i,j in kwargs.items():
print(i,j)
func(a=1,b=2,c=3)
#输出
{'a': 1, 'b': 2, 'c': 3}
a 1
b 2
c 3
上例表示函数接收任意多个关键字参数,然后在执行函数时将输入的关键字参数打包成“字典”kwargs。
def func(**kwargs):
print(kwargs)
print(type(kwargs))
for i,j in kwargs.items():
print(i,j)
d = {'a':1,'b':2,'c':3}
func(**d)
#输出
{'a': 1, 'b': 2, 'c': 3}
a 1
b 2
c 3
上例同样表示函数接收任意多个关键字参数,不同的是执行函数时用了**d,他表示先将{'a':1,'b':2,'c':3}解包成a=1,b=2,c=3,即成为关键字参数,然后将他们打包成“字典”kwargs。
PS:注意,使用这种方法将字典传入函数的时候,字典的键的命名要符合python变量的命名规则,通过上面的分析也不难看出,双星号会将字典首先转换成关键字参数的形式,就相当于使用字典中的键作为变量名,如果键不符合变量命名规则,则会抛出一个"TypeError"异常,大家可以尝试着颠倒一下上面字典中的键和值,使用数字作为键,看看会出现什么问题。
def func(arg,*args,b=0,**kwargs):
print(arg)
print(args)
print(b)
print(kwargs)
func(1,2,3,4,b=100,c=5,d=6)
#输出
1
(2, 3, 4)
100
{'c': 5, 'd': 6}
上例是混合型函数,可以看出,不加星号的参数,是按照位置参数(非关键字参数)传递,而且必须有一个参数传递,*args位置可以接收任意多个非关键字参数,**kwargs位置可以接收任意多个关键字参数。注意,两者在函数定义中的顺序不能颠倒。
(4) 单星号*分隔关键字参数和非关键字参数
在一个函数的接收参数中,同时出现"非关键字参数(位置参数)"和"关键字参数"时,可以使用一个单星号*来分隔这两种参数:
下例中,星号前面的a、b表示非关键字参数,而星号后面的c、d表示关键字参数(虽然它们没有带=),调用时需要带上=:
def func(a, b, *, c, d):
print(a, b, c, d)
func(1,2,c=3,d=4) #正确
1 2 3 4
func(1,2,3,4) #报错
TypeError: func() takes 2 positional arguments but 4 were given
如果位置参数与关键字参数之间已经存在了一个“单星号位置参数”,则这个参数后面的就都是关键字参数,且不需要再使用星号来分隔他们了:
下例中b是关键字参数(虽然它们没有带=)
def func(a,*args,b):
print(a)
print(args)
print(b)
func(1,2,3,4,b=6) #正确
#输出
1
(2, 3, 4)
6
func(1,2,3,4,b=6) #报错
TypeError: func() missing 1 required keyword-only argument: 'b'
在函数定义中,
单星号前面的都是位置参数(非关键字参数),无论参数是否带=;
单星号后面的都是关键字参数,无论是否带=;
且这里的单星既包括独立的星号*,也包括单星号参数,例如*args:
下例中虽然参数a带=,但位于独立星号前,因此属于位置参数;而b虽然不带=,但位于独立星号后,因此舒适与关键字参数:
def func(a=0,*,b):
print('a=',a)
print('b=',b)
func(1,b=2)
a= 1
b= 2
下例中,a=0在单星号变量b前面,因此属于位置参数,而c在b后面,因此属于关键字参数:
def func(a=0,*b,c):
print('a=',a)
print('b=',b)
print('c=',c)
func(1,2,3,4,c=5)
a= 1
b= (2, 3, 4)
c= 5
总结
如果我们要在一个函数中包含多种参数的组合,必须遵守这样的顺序:位置参数(必选参数),默认参数,单星号参数或星号分隔符,关键字参数,双星号参数;请看下面的实例:
(注意把位置参数b=0,当成了默认参数)
这里需要注意的是定义中b=0是位置参数,我们在使用该函数时并没有明确写出b=2,但从结果可以看出,依然将2赋值给了b,进一步说明了b是位置参数。
def func(a,b=0,*c,x,**y):
print('a=',a)
print('b=',b)
print('c=',c)
print('x=',x)
print('y=',y)
func(1,2,3,4,5,x=6,y=7,z=8)
a= 1
b= 2
c= (3, 4, 5)
x= 6
y= {'y': 7, 'z': 8}