python3中函数参数有位置参数、命名关键字参数、默认参数、非关键字可变长参数、关键字可变长参数几种。
一、位置参数
位置参数是函数的标准参数,是最常用的一种参数格式。定义函数时,由于每个参数都有自己的位置,如果不加以特殊说明,调用函数时,函数就会根据所赋参数的位置来给函数内参数赋值。
def s(x,y): print('x=',x) print('y=',y)a,b=1,0s(a,b)运行结果:x= 1y= 0
调用时,函数会自动将处在x位置的a参数赋给x,把参数b赋给y。
位置参数是必选参数,调用函数时,位置参数未赋值,函数将报错。
二、命名关键字参数
格式: def s(x,y,*,z,k) print('x=',x) print('y=',y) print('z=',z) print('k=',k)
a,b,c,d=4,3,2,1 s(a,b,k=c,z=d) 输出 x= 4 y= 3 z= 1 k= 2
此时,位置参数必须在最前,关键字参数在后,关键字参数之间可以不按顺序。
定义命名关键字参数时,需要使用*分割符,*后面的参数都是命名关键字参数。命名关键字参数在使用时,必须使用‘形参=’这种格式,不然将会报错
三、默认参数
默认参数是指在定义函数时,赋给该参数一个值。调用该函数时,如果未给该参数赋值,则函数会自动将定义时的值拿来使用。
def s(x,k='233'): print('x=',x) print('k=',k) a,b,c,d=4,3 s(a) s(a,b) s(a,k=c) 输出: x= 4 k= 233 x= 4 k= 3 x= 4 k= 4
可以看到,如果省略了k,则默认为是233,如果给k赋值,则以赋值为准。给k赋值时,可以采用位置参数的方式(不加想形参名字),也可以采取关键字参数的方式。 默认参数在定义时的位置必须在位置参数之后。调用的时候,也必须在位置参数之后,否则会报错。这是因为函数在给参数赋值的时候,会优先将值赋给位置参数,剩下如果有多余的值会再赋给默认参数,如果没有就采用默认值。
注意,定义默认参数一定是不可变参数。如果是可变参数,可能会出现一些错误。参考廖雪峰老师微博:
先定义一个函数,传入一个list,添加一个END
再返回:
def add_end(L=[]): L.append('END') return L 当你正常调用时,结果似乎不错: >>> add_end([1, 2, 3]) [1, 2, 3, 'END']
>>> add_end(['x', 'y', 'z']) ['x', 'y', 'z', 'END']
当你使用默认参数调用时,一开始结果也是对的:
>>> add_end()['END']
但是,再次调用add_end()
时,结果就不对了:
>>> add_end()['END', 'END']>>> add_end()['END', 'END', 'END']
很多初学者很疑惑,默认参数是[]
,但是函数似乎每次都“记住了”上次添加了'END'
后的list。
原因解释如下:
Python函数在定义的时候,默认参数L
的值就被计算出来了,即[]
,因为默认参数L
也是一个变量,它指向对象[]
,每次调用该函数,如果改变了L
的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]
了。
要修改上面的例子,我们可以用None
这个不变对象来实现:
def add_end(L=None): if L is None: L = [] L.append('END') return L
现在,无论调用多少次,都不会有问题:
>>> add_end()['END']>>> add_end()['END']
为什么要设计str
、None
这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
如果函数中既有命名关键字参数,又有默认参数,两个参数必须都在位置参数之后。如果赋值时,默认参数赋值不使用‘k=’这样的格式,则必须严格按照定义函数时的位置来传递参数。如果使用‘k=’这种格式,在这两只参数在传递时位置没有特殊的要求。
但是在定义函数时,如果将默认参数放在命名关键参数之后,则表明这个参数既是默认参数,也是命名关键参数。
def s(x,k=233,*,y,z,j=111): print('x=',x) print('y=',y) print('z=',z) print('k=',k) print('j=',j)a,b,c,d=4,3,2,1s(a,z=c,y=b,k=d)输出:x= 4y= 3z= 2k= 1j=111
四、非关键字可变长参数
非关键字可变长参数用 *变量名 来表示,定义函数时,一般用 *args 来表示。主要用来处理一些不能确定有多少个参数的问题。
函数将多余的不带关键字的参数打包成为一个元组,进行处理。*args后面的参数必须使用关键字,不然全都会被当成可变长参数,被打包到元组中。
但是,*args与默认参数之间顺序的问题也需要注意。
def s(x,y=233,*args): print('x =',x) print('y =',y) for index,i in enumerate(args): print('args[%s] = %s'%(index,i)) print(type(args)) s(1,2,3,4,5) 输出:
x = 1
y = 2args[0] = 3args[1] = 4args[2] = 5<class 'tuple'>可以看到,该函数第一个值赋给位置参数,第二个值赋给默认参数,后面的值全都打包成为一个元组,通过for循环来显示。只要传递的参数传递的参数大于两个,就会自动给默认参数赋值,此时默认参数基本上失去了默认的意义。
也可以将默认参数放在*args的后面,但是此时给默认参数赋值时,需要加上关键字,不然就会被打包为元组,赋值给args。
def s(x,*args,y=233): print('x =',x) print('y =',y) for index,i in enumerate(args): print('args[%s] = %s'%(index,i))s(1,2,3,4,5)输出:x = 1y = 233args[0] = 2args[1] = 3args[2] = 4args[3] = 5 s(1,2,3,4,y=5)输出:x = 1y = 5args[0] = 2args[1] = 3args[2] = 4
列表或元组可以以作为参数传入*args,如果不想被二次打包,需要在列表或者元组前面加 * 。
def s(x,*args,y=233): print('x =',x) print('y =',y) for index,i in enumerate(args): print('args[%s] = %s'%(index,i))s(1,2,*[4,5,6])输出:x = 1y = 233args[0] = 2args[1] = 4args[2] = 5args[3] = 6
五、可变长的关键字参数
可变长关键字参数用 **变量名 来表示,定义函数时,一般用 **kwargs 来表示。主要用来处理一些不能确定有多少个关键字参数的问题。
函数将多余的带关键字的参数打包成为一个字典,关键字为字典的key,进行处理。**kwargs必须是放在函数形参的最后。
def s(x,*args,y=233,**kwargs): print('x =',x) print('y =',y) for index,i in enumerate(args): print('args[%s] = %s'%(index,i)) for i in kwargs: print(i,' ',kwargs[i]) print('kwargs type is ',type(kwargs))s(1,2,a=3,b=4)输出结果:x = 1y = 233args[0] = 2a 3b 4kwargs type is
同样,字典也可以作为参数传入**kwargs,需要在字典前加 **
可以对比下列代码的执行结果:
def s(x,*args,y=233,**kwargs): print('x =',x) print('y =',y) for index,i in enumerate(args): print('args[%s] = %s'%(index,i)) for i in kwargs: print(i,' ',kwargs[i])s(1,2,{ 'a':5})输出:x = 1y = 233args[0] = 2args[1] = { 'a': 5} #由于该字典不带关键字,会被当成一个非关键字参数打包入args。
s(1,2,a={ 'a':5}) 输出: x = 1 y = 233 args[0] = 2 a { 'a': 5} # 此处,该字典带了关键字,会将该字典连同关键字一起进行打包,变为一个嵌套的字典。 s(1,2,**{ 'a':5}) 输出: x = 1 y = 233 args[0] = 2 a 5 #由于字典前加上** ,该字典直接被加入kwargs中