Table of Contents
Python 中函数参数传递的几种方法
Python 中函数参数的传递,本质上是把对象绑定到函数的形参名上,也可以理解为通过“赋值”来传递。这个规则回答了参数传递的总体原则,但还需要进一步说明:函数参数如何定义,以及函数调用时参数如何解析。
函数参数的定义常见有四种形式:
F(arg1, arg2, ...)F(arg2=<value>, arg3=<value>, ...)F(*args)F(**kwargs)
1. 普通位置参数
第一种方式是最传统的方式。函数可以定义若干个参数,形式参数放在函数名后的小括号中,各个参数之间用逗号隔开。
用这种方式定义的函数,在调用时必须提供相同数量的实际参数,不能多也不能少,并且顺序必须对应。也就是说,形参和实参之间是一一对应的关系:形参1 = 实参1,形参2 = 实参2,依此类推。
例如:
def add_on(x, y):
return x + y
这个函数可以这样调用:
add_on(1, 2)
这表示形参 x 取值为 1,形参 y 取值为 2。而下面两种调用方式都是错误的:
add_on(1, 2, 3) # 参数太多
add_on(1) # 参数太少
2. 默认参数与关键字参数
第二种方式是在定义函数时给形参指定默认值。调用函数时,如果没有给对应的形参传递实参,那么这个形参就使用默认值。
例如:
def add_on(x=3, y=5):
return x + y
调用:
add_on(6, 5)
表示 x 取值为 6,y 取值为 5。
调用:
add_on(7)
表示 x 取值为 7,y 使用默认值 5。
这时会出现一个问题:如果想让 x 使用默认值,只给 y 赋值怎么办?这就要用到 Python 函数调用中的关键字参数:
add_on(y=6)
这表示 x 使用默认值 3,y 取值为 6。
关键字参数可以直接指定形参名,因此不必遵守参数定义时的先后顺序。例如:
add_on(y=4, x=6)
这种通过形参名进行定点赋值的方式,对于只使用普通位置参数定义的函数同样适用。
需要注意的是,默认参数有一个常见陷阱:不要把可变对象作为默认值,例如列表、字典等。因为默认值只会在函数定义时创建一次,后续调用会复用同一个对象。
3. 可变位置参数 *args
前两种方式定义的形参个数都是固定的。例如定义函数时如果定义了 5 个形参,那么调用时最多也只能传递 5 个实参。
但实际编程中,并不总能提前确定一个函数会接收多少个参数。第三种方式就是用来应对这种情况的。它使用一个星号加形参名表示,例如 *args。这个函数可以接收任意数量的位置参数,可以是 0 个,也可以是 N 个。传入的这些参数会在函数内部组成一个元组。
例如:
def add_on(*args):
total = 0
for value in args:
total += value
return total
下面这些调用方式都是可以的:
add_on()
add_on(2)
add_on(3, 4, 5, 6)
在函数内部,args 分别对应:
()
(2,)
(3, 4, 5, 6)
4. 可变关键字参数 **kwargs
与第三种方式类似,形参名前面加两个星号,表示接收任意数量的关键字参数。这些参数会在函数内部组成一个字典。
调用这种函数时,必须采用 key=value 的形式传参。例如:
def add_on(**kwargs):
total = 0
if len(kwargs) == 0:
return 0
for value in kwargs.values():
total += value
return total
调用方式可以是:
add_on()
add_on(x=4, y=5, k=6)
其中 add_on(x=4, y=5, k=6) 在函数内部得到的 kwargs 是:
{'x': 4, 'y': 5, 'k': 6}
如果使用 Python 2,字典遍历也可以写成 kwargs.itervalues();在 Python 3 中应使用 kwargs.values()。
组合使用参数
上面四种参数定义方式可以组合在一起,形成更复杂也更灵活的函数定义形式。定义函数时通常要遵循下面的顺序:
- 普通位置参数,例如
arg - 默认参数,例如
arg=<value> - 可变位置参数,例如
*args - 可变关键字参数,例如
**kwargs
也就是说:
def func(arg, default_arg=value, *args, **kwargs):
pass
在函数调用过程中,形参赋值的大致过程是:
- 先按顺序把普通位置实参赋给对应的形参。
- 再把
arg=<value>这种形式的关键字实参赋给对应形参。 - 多出来的位置实参组成一个元组,赋给带一个星号的形参。
- 多出来的关键字实参组成一个字典,赋给带两个星号的形参。
听起来复杂,实际很直观。看下面这个例子:
def test(x, y=5, *a, **b):
print(x, y, a, b)
不同调用方式的结果如下:
test(1) # 1 5 () {}
test(1, 2) # 1 2 () {}
test(1, 2, 3) # 1 2 (3,) {}
test(1, 2, 3, 4) # 1 2 (3, 4) {}
test(x=1) # 1 5 () {}
test(x=1, y=1) # 1 1 () {}
test(x=1, y=1, a=1) # 1 1 () {'a': 1}
test(x=1, y=1, a=1, b=1) # 1 1 () {'a': 1, 'b': 1}
test(1, y=1) # 1 1 () {}
test(1, 2, y=1) # TypeError: test() got multiple values for argument 'y'
test(1, 2, 3, 4, a=1) # 1 2 (3, 4) {'a': 1}
其中 test(1, 2, y=1) 会出错,因为位置参数 2 已经赋给了 y,后面又通过关键字参数 y=1 再次给 y 赋值,因此 Python 会提示 y 获得了多个值。
总结一下:普通位置参数适合固定参数数量的函数;默认参数适合给常用值设置缺省行为;args 适合接收不定数量的位置参数;kwargs 适合接收不定数量的关键字参数。实际编写函数时,应尽量让参数设计清晰,只有在确实需要灵活扩展时再使用 args 和 **kwargs。
