定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解。
Python中的函数定义虽然简单,但是灵活度却很大,这里来总结一下。python中函数传递的方式大致有以下四种方式:
位置参数(必选参数)
默认参数
可变参数
关键字参数
对应函数写法为:
fun1(a,b,c)
fun2(a=1,b=2,c=3)
fun3(*args)
fun4(**kargs)
第一种 fun1(a,b,c)
是直接将实参赋予行参,根据位置做匹配,即严格要求实参的数量与行参的数量位置相等,大多数函数采用这种写法。
第二种 fun2(a=1,b=2,c=3)
根据键值对的形式来实行实参与行参的匹配,通过这种方式可以忽略参数的位置关系,直接根据关键字来进行赋值,即可以使用fun2(3,4)
来调用fun2函数,等同于fun2(a=3,b=4,c=3)
,这里关键就是参数3,4覆盖了原来a、b两个行参的值,但c还是采用原来的默认值3,这种模式相较第一种更加灵活,不仅可以通过fun2(c=5,a=2,b=7)
来打乱行参的位置,而且可以在没有对应行参传递的时候采用参数默认值。
第三种 fun3(*args)
,这种传参方式可以传入任意个参数,这些若干参数都被放到了tuple元组中赋值给行参args,然后在函数中使用这些行参,直接操作args这个tuple元组就可以了,这样的好处是在参数的数量上没有了限制。
第四种 fun4(**kargs)
最为灵活,其是以键值对字典的形式向函数传参,含有第二种位置的灵活的同时具有第三种方式的数量上的无限制。
最后要强调的是四种传递方式若混合使用,参数定义的顺序必须是:必选参数、默认参数、可变参数、关键字参数。
举例:
定义一个函数同时接收四种参数:
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) ===> 出错,说y给赋了多个值
test(1,2,3,4,a=1) ===> 1 2 (3,4) {'a':1}
test(1,2,3,4,k=1,t=2,o=3) ===> 1 2 (3,4) {'k':1,'t':2,'o':3}
总结:
Python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。
默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
要注意定义可变参数和关键字参数的语法:
*args
是可变参数,args接收的是一个tuple;
**kw
是关键字参数,kw接收的是一个dict。
以及调用函数时如何传入可变参数和关键字参数的语法:
可变参数既可以直接传入:func(1, 2, 3)
,又可以先组装list或tuple,再通过*args
传入:func(*(1, 2, 3))
;
关键字参数既可以直接传入:func(a=1, b=2)
,又可以先组装dict,再通过**kw
传入:func(**{'a': 1, 'b': 2})
。
使用*args
和**kw
是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。(这里提到的命名关键字本文未提及)
定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。