上一节说了参数位置和默认参数,这一节着重说可变参数和关键字参数
可变参数
可变参数就是传入的参数个数是可变的,可以任意个参数,包括0个参数。
我们以计算a2 + b2 + c2 + ……为例
要定义这个函数,我们可以把需要传入的参数以一个list或者tuple传入,定义如下,这里我们用到了for循环,从list或tuple中循环取值,执行命令:
>>> def f(num): sum=0 for n in num: sum=sum+n*n return sum >>> f((1,2,3)) 14 >>> f([2,3,4]) 29 >>> f(1,2,3) Traceback (most recent call last): File "<pyshell#6>", line 1, in <module> f(1,2,3) TypeError: f() takes 1 positional argument but 3 were given
我们看到传入的参数为list和tuple时可以很好的完成任务,但是我们想简单点直接写一组数字就报错了。这个时候就需要用到可变参数。在定义时,num前加*号,这样函数会把传入的参数按一个tuple接收:
>>> def f(*num): sum=0 for n in num: sum=sum+n*n return sum >>> f(1,2,3) 14 >>> f(2,3,4) 29 >>>
那么如果说我们已经有一个list或者tuple,想对整个list或tiple调用函数f该如何调用呢,一种方法是直接用list的定位来完成参数传入:
>>> l=[3,4,5] >>> f(l[0],l[1],l[2]) 50 >>> s=(3,4,5) >>> f(s[0],s[1],s[2]) 50
这样操作显然有些麻烦,如果list里内容太多也一样写不过来,这时可以用第二种方法,可以直接在函数调用时,在list或者tuple前面加*进行传参,如下:
>>> f(*l) 50 >>> f(*s) 50
关键字参数
关键词参数允许函数传入0个或多个含有参数名的参数,在函数内部组装为一个字典dict
>>> def person(name,age,**other): print('name:',name,'age:',age,'other',other) >>> person('张三',35) name: 张三 age: 35 other {} >>> person('李四',36,city='北京') name: 李四 age: 36 other {'city': '北京'} >>> person('王五',40,city='北京',site='www.woodmangzhang.com') name: 王五 age: 40 other {'city': '北京', 'site': 'www.woodmangzhang.com'}
上面这个示例有点像我们在用户注册时,有些是必填信息,有些是非必填项,但是如果用户愿意提交就都可以提交。
关键词参数也可以调用已经组装好的dict来完成传参,如下:
>>> others={'city':'北京','site':'www.woodmangzhang.com'} >>> person('赵六',29,**others) name: 赵六 age: 29 other {'city': '北京', 'site': 'www.woodmangzhang.com'}
可变参数是一个*,关键词参数是两个*,在定义和调用时都是一样的用法,只不过一个是list或tuple,一个是dict。上面的例子中注意others是一个外部的dict,函数内部的other只是从外部的others取值,内部other的改变并不影响外部的others。
命名关键词参数
那用了关键词参数,使用者不是随便传参数内容进来,这就需要函数内部做检查,仍然以上面的问题为例,我们要检查是否有city和site:
>>> def person(name,age,**other): if 'city' in other: pass if 'site' in other: pass print(name,age,other) >>> person('张三',15,city='北京',site='www.woodmanzhang.com') 张三 15 {'city': '北京', 'site': 'www.woodmanzhang.com'} >>> person('张三',15,city='北京',site='www.woodmanzhang.com',homeadd='中关村壹号') 张三 15 {'city': '北京', 'site': 'www.woodmanzhang.com', 'homeadd': '中关村壹号'}
可以看到调用者依然可以随意传入数据,如果我们要限制只接受city和site作为关键词参数,可以用*,关键词1,关键词2表示,也就是命名关键词:
>>> def person(name,age,*,city,site): print(name,age,city,site) >>> person('张三',21,city='北京',site='www.woodmanzhang.com') 张三 21 北京 www.woodmanzhang.com >>> person('张三',21,city='北京',site='www.woodmanzhang.com',homeadd='中关村壹号') Traceback (most recent call last): File "<pyshell#64>", line 1, in <module> person('张三',21,city='北京',site='www.woodmanzhang.com',homeadd='中关村壹号') TypeError: person() got an unexpected keyword argument 'homeadd' >>> person('木人张',35) Traceback (most recent call last): File "<pyshell#66>", line 1, in <module> person('木人张',35) TypeError: person() missing 2 required keyword-only arguments: 'city' and 'site' >>> person('木人张',35,'北京','www.woodmangzhang.com') Traceback (most recent call last): File "<pyshell#70>", line 1, in <module> person('木人张',35,'北京','www.woodmangzhang.com') TypeError: person() takes 2 positional arguments but 4 were given
可见,当设定了命名关键词参数,对应的参数就成了必选参数,当我们多输入和少输入命名关键词参数时都会报错;对于命名关键词参数,不按关键词传入也一样会报错!因为没有加关键词传入,函数理解为传入了四个位置参数,但定义函数时指指定了2个位置参数。
而如果函数中已经有了一个可变参数,那么后面跟着的命名关键词参数就都不再需要*来分隔:
>>> def person(name,age,*s,city,site): print(name,age,s,city,site)
命名关键词还可以设定缺省默认值,从而简化调用时的传入,如下有了默认值,我们传参时可以不传入这一项:
>>> def person(name,age,*,city='北京',site): print(name,age,city,site) >>> person('张三',18,site='woodmanzhang.com') 张三 18 北京 woodmanzhang.com
需要注意的是,*的区隔必须有,可以是一个可变参数,或者没有可变参数用*, 如果没有*这一标识,程序会认为需要传入的都是位置参数,无法识别关键词参数。
另外,关键词参数是可以调换顺序的,这也是它跟位置参数的一个区别,如下:
>>> person('张三',18,site='woodmanzhang.com',city='上海') 张三 18 上海 woodmanzhang.com
参数组合
必须参数、默认参数、可变参数、命名关键字参数、关键字参数可以组合使用,即为参数组合,注意参数定义的顺序,也要按这一顺序。
>>> def f1(a,b,c=0,*args,**kw): print(a,b,c,args,kw) >>> def f2(a,b,c=0,*,d,**kw): print(a,b,c,d,kw) >>> f1(1,2,3) 1 2 3 () {} >>> f2(1,2,3) >>> f2(1,2,3,d=4,n=5) 1 2 3 4 {'n': 5} >>> f1(1,2,3,('a','b'),'nono') 1 2 3 (('a', 'b'), 'nono') {} >>> args = (1, 2, 3, 4) >>> kw = {'d': 99, 'x': '#'} >>> args=(1,2,3,4) >>> kw={'d':99,'e':100} >>> f1(*args,**kw) 1 2 3 (4,) {'d': 99, 'e': 100} >>> args=(1,2,3) >>> f2(*args,**kw) 1 2 3 99 {'e': 100} >>> args=[1,2,3] >>> f2(*args,**kw) 1 2 3 99 {'e': 100}
函数调用时,不论是手动传入的参数,还是list或者tuple和dict,都可以给函数传参调用。