Python函数的参数传递(二)
可变数量参数
在Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。
我们以数学题为例子,给定一组数字a,b,c……,请计算a2 + b2 + c2 + ……。
要定义出这个函数,我们必须确定输入的参数。由于参数个数不确定,我们首先想到可以把a,b,c……作为一个list或tuple传进来,这样,函数可以定义如下:
>>> def calc(numbers): sum = 0 for n in numbers: sum = sum + n * n return sum
但是调用的时候,需要先组装出一个list或tuple:
>>> print(calc([1, 2, 3])) 14 >>> print(calc((1, 3, 5, 7))) 84
如果利用可变参数,调用函数的方式可以简化成这样:
>>> calc(1, 2, 3) 14 >>> calc(1, 3, 5, 7) 84
所以,我们把函数的参数改为可变参数:
def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum
定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:
>>> calc(1, 2) 5 >>> calc() 0 >>>calc(1,2,3,4,5)
如果已经有一个list或者tuple,直接在list或tuple前面加一个*号就行,把list或tuple的元素变成可变参数传进去:
>>> nums = [1, 2, 3] >>> calc(*nums) 14
- nums表示把nums这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。
关键字参数
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。请看示例:
def person(name, age, **kw): print('name:', name, 'age:', age, 'other:', kw)
函数person除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数:
>>> person('Michael', 30) name: Michael age: 30 other: {}
也可以传入任意个数的关键字参数:
>>> person('Bob', 35, city='Beijing') name: Bob age: 35 other: {'city': 'Beijing'} >>> person('Adam', 45, gender='M', job='Engineer') name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
关键字参数有什么用?它可以扩展函数的功能。比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。
和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:
>>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, city=extra['city'], job=extra['job']) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
当然,上面复杂的调用可以用简化的写法:
>>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, **extra) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。
练习:用关键字参数定义一个函数student,#参数列表为 姓名、学号(必选),其他信息为可选
- 调用函数,打印您的姓名和学号
- 调用函数,打印您的姓名和学号和家乡
- 调用函数,打印您的姓名、学号、家乡和爱好
命名关键字参数
对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:
def person(name, age, *, city, job): print(name, age, city, job)
和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。
调用方式如下:
>>> person('Jack', 24, city='Beijing', job='Engineer') Jack 24 Beijing Engineer
参数组合
在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
比如定义一个函数,包含上述若干种参数:
def f1(a, b, c=0, *args, **kw): print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw) def f2(a, b, c=0, *, d, **kw): print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
在函数调用的时候,Python解释器自动按照参数位置和参数名把对应的参数传进去。
>>> f1(1, 2) a = 1 b = 2 c = 0 args = () kw = {} >>> f1(1, 2, c=3) a = 1 b = 2 c = 3 args = () kw = {} >>> f1(1, 2, 3, 'a', 'b') a = 1 b = 2 c = 3 args = ('a', 'b') kw = {} >>> f1(1, 2, 3, 'a', 'b', x=99) a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99} >>> f2(1, 2, d=99, ext=None) a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
- 几种不同类型的参数可以混合使用,但是不建议这样做。
传递参数时的序列解包(可选)
- 传递参数时,可以通过在实参序列前加一个星号将其解包,然后传递给多个单变量形参。
>>> def demo(a, b, c): print(a+b+c) >>> seq = [1, 2, 3] >>> demo(*seq) 6 >>> tup = (1, 2, 3) >>> demo(*tup) 6 >>> dic = {1:'a', 2:'b', 3:'c'} >>> demo(*dic) 6 >>> Set = {1, 2, 3} >>> demo(*Set) 6 >>> demo(*dic.values()) abc
- 如果函数实参是字典,可以在前面加两个星号进行解包,等价于关键参数。
>>> def demo(a, b, c): print(a+b+c) >>> dic = {'a':1, 'b':2, 'c':3} >>> demo(**dic) 6 >>> demo(a=1, b=2, c=3) 6 >>> demo(*dic.values()) 6
- 注意:调用函数时对实参序列使用一个星号*进行解包后的实参将会被当做普通位置参数对待,并且会在关键参数和使用两个星号**进行序列解包的参数之前进行处理。
>>> def demo(a, b, c): #定义函数 print(a, b, c) >>> demo(*(1, 2, 3)) #调用,序列解包 1 2 3 >>> demo(1, *(2, 3)) #位置参数和序列解包同时使用 1 2 3 >>> demo(1, *(2,), 3) 1 2 3 >>> demo(a=1, *(2, 3)) #序列解包相当于位置参数,优先处理 Traceback (most recent call last): File "<pyshell#26>", line 1, in <module> demo(a=1, *(2, 3)) TypeError: demo() got multiple values for argument 'a' >>> demo(b=1, *(2, 3)) Traceback (most recent call last): File "<pyshell#27>", line 1, in <module> demo(b=1, *(2, 3)) TypeError: demo() got multiple values for argument 'b' >>> demo(c=1, *(2, 3)) 2 3 1 >>> demo(**{'a':1, 'b':2}, *(3,)) #序列解包不能在关键参数解包之后 SyntaxError: iterable argument unpacking follows keyword argument unpacking >>> demo(*(3,), **{'a':1, 'b':2}) Traceback (most recent call last): File "<pyshell#30>", line 1, in <module> demo(*(3,), **{'a':1, 'b':2}) TypeError: demo() got multiple values for argument 'a' >>> demo(*(3,), **{'c':1, 'b':2}) 3 2 1