*、**、*args、**kwargs、函数参数顺序

(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}

你可能感兴趣的:(*、**、*args、**kwargs、函数参数顺序)