python 函数的参数可分为位置参数、缺省参数、可变参数和关键字参数。其中可变参数和关键字参数又可以进行拆包。
位置参数
看下面程序:
def fun1(a, b):
print("a -- " + str(a))
print("b -- " + str(b))
return a + b
print(fun1(2, 3))
显然,程序结果:
a ---- 2
b ---- 3
5
这里的函数 fun1 中的参数 a 和 b 就是位置参数。位置参数在调用时,必须给到具体值。
缺省参数
看下面的代码:
def fun2(a, b, c=0):
print("a -- " + str(a))
print("b -- " + str(b))
print("c -- " + str(c))
return a + b + c
print(fun2(2, 3))
print(fun2(2, 3, 4))
程序输出结果:
a -- 2
b -- 3
c -- 0
5
a -- 2
b -- 3
c -- 4
9
这里函数 fun2 中,有三个参数,其中 a 和 b 是位置参数, c 即是一个缺省参数。声明函数 fun2 时指定了参数 c=0 ,表示参数 c 的默认值是 0 ,上面程序中两次在调用 fun2 时,分别传入了两个参数和三个参数。我们看到,在传入两个参数时,a 和 b 的值分别是传入的 2 和 3,其中 c 的值为默认值 0 ,在传入三个参数时,a, b 和 c
值分别是传入的 2, 3, 4 。
很好理解,在函数定义的时候,给参数赋了一个默认值,这样的参数就叫缺省参数,也叫默认参数。
假设有多个缺省参数,而我只传入了部分缺省参数会是怎样的情况?看下面的程序:
def fun2(a, b, c=0, d=0):
print("a -- " + str(a))
print("b -- " + str(b))
print("c -- " + str(c))
print("d -- " + str(d))
return a + b + c + d
print(fun2(2, 3, 4))
程序执行结果如下:
a -- 2
b -- 3
c -- 4
d -- 0
9
说明,前面的位置参数是必须要传入的,c 的值是 4 ,而 d 的值是默认值 0 ,这说明,传入的部分缺省参数会按顺序赋值。如果偏要将 4 赋给 d ,而 c 用默认参数呢,则用下面方式调用函数即可:print(fun2(2, 3, d=4))
程序结果:
a -- 2
b -- 3
c -- 0
d -- 4
9
不止是缺省参数,位置参数也可以在调用函数时,也可以指定具体的值赋给哪个参数。
值得注意的是:定义函数时缺省参数要放在位置参数后面,如果向下面这样定义:
def fun2(a, b=0, c):
return a + b + c
则会报错:SyntaxError: non-default argument follows default argument
熟悉 java、C# 或者 C++ 等语言的应该知道“重载方法”的概念。参数列表不相同,包括参数的类型不相同和参数的个数不相同。在python中,用缺省参数,正好可以实现重载类似的效果。
可变参数(不定长参数)
在Python函数中,还可以定义可变参数,也叫不定长参数。顾名思义,可变参数就是传入的参数个数是可变的,写法为,在参数前面加上一个 *
,类似于 java 中的不定长参数。不同的是,java中不定长参数,以数组形式存储,在python中,可变参数则是一个元组。看下面的程序:
def fun3(a, *b):
print(type(a))
print(a)
print(type(b))
print(b)
fun3(1, 2, 3, 4, 5)
输出结果如下:
<class 'int'>
1
<class 'tuple'>
(2, 3, 4, 5)
程序中,函数 fun3 的参数 a 是一个 int 型,后面 *b 即为可变参数,在调用时,可以根据需要传入不同个数的参数,除去 a 之后,剩余所有的参数,都添加到一个元组中。
看下面的调用:fun3(1, 2, (3, 4, 5), [6, 7], {'x': 8, 'y': 9}, 10, 11)
输出结果如下:
<class 'int'>
1
<class 'tuple'>
(2, (3, 4, 5), [6, 7], {'x': 8, 'y': 9}, 10, 11)
结果显示很明确了,即便我传入列表,元组,字典类型的参数,也会被当做元组的元素,添加进去。
约定俗成,函数中的可变参数一般用 *args
来表示。
关键字参数
最后说说 python 的关键字参数。python 关键字参数是在参数前面加上 **
,和可变参数有点类似,区别在于,关键字参数是以字典形式存储。调用的时候也有区别,看下面的程序:
def fun4(a, *b, **c):
print(type(c))
print(a)
print(b)
print(c)
fun4(1, 2, 3, 4, 5, x=6, y=7)
程序结果:
<class 'dict'>
1
(2, 3, 4, 5)
{'x': 6, 'y': 7}
结果显示,参数 C 的类型是 dict 字典。在调用时,需要按照 key = value
的形式传入关键字参数。约定俗成,关键字参数一般用 **kwargs
来表示。按照上面的写法,我们调用函数时可以随意传入关键字参数,有时候,我们需要限定关键字参数的 'key' ,这样我们可以使用命名关键字参数。比如,上面的程序,我在调用时关键字参数,传入了 'x' 和 'y',事实上,我想的话,还可以传入更多的参数 ,用命名关键字参数,则可以限定,只传入 'x' 和 'y' ,程序如下:
def fun5(a, *, x, y):
print(a)
print(x)
print(y)
fun5(1, x=2, y=3)
输出结果:
1
2
3
即用一个 *,
,后面跟上指定的参数。这样,你如果多传,或者少穿关键字参数,都是不可以的。
可变参数和关键字参数拆包
再回头看前面的例子:
def fun3(a, *b):
print(type(a))
print(a)
print(type(b))
print(b)
fun3(1, 2, 3, 4, 5)
执行 fun3(1, 2, (3, 4, 5), [6, 7], {'x': 8, 'y': 9}, 10, 11)
输出结果如下:
<class 'int'>
1
<class 'tuple'>
(2, (3, 4, 5), [6, 7], {'x': 8, 'y': 9}, 10, 11)
这里,传入参数的 (3,4,5) 和 [6,7] 和 {'x': 8, 'y': 9} 都被当作元组里面的元素了。下面在调用时,参数前面添加一个 *
,如下:fun3(1, 2, *(3, 4, 5), *[6, 7], *{'x': 8, 'y': 9}, 10, 11)
结果如下:
<class 'int'>
1
<class 'tuple'>
(2, 3, 4, 5, 6, 7, 'x', 'y', 12, 13)
看结果,在列表或者元组参数前面添加 *
之后,却反而被拆开成单个元素,作为元组的元素。而字典,只是把它的 'key' 拆成单个元素。这就是可变参数的拆包。一般,可变参数的拆包我们只针对元组和列表,不针对字典。
同样,对于含有关键字参数的函数,也可对传入的参数进行拆包,看下面这个函数:
def fun6(**kwargs):
print(kwargs)
下面我调用函数 fun6({'x': 1, 'y': 2})
,这时候,程序会报错
因为,关键字参数,在调用的时候传参,需要按照 key = value
的形式传入。而我现在传入一个字典,会被当做一个整体,作为一个位置参数传入。如果以下面这种方式fun6(**{'x': 1, 'y': 2})
调用,则可行,结果如下:
{'x': 1, 'y': 2}
即通过 **
将字典拆成了 'x'=1 , 'y'=2
的形式。这就是关键字参数的拆包。