5分钟搞定Python中函数的参数

Python (233) 2023-04-20 14:47:13

函数的灵活性非常高,除了常规定义的位置参数以外,还支持默认参数、关键字参数、以及可变参数 ... 这样以来,不但能应对各种复杂的情况,甚至还可以简化调用者的代码。

位置参数

在调用函数时,一般会根据函数定义的参数位置来传递参数,这样的参数叫做位置参数。

考虑下面的例子,函数包含了两个参数 - name 和 msg:

>>>defgreet(name,msg):
...print('Hello,{},{}!'.format(name,msg))
...>>>

调用它很容易:

>>>greet('Pony','nicetomeetyou')
Hello,Pony,nicetomeetyou!

这里,值会按照顺序被依次分配。由于“Pony”是第一个值,所以它会被分配给第一个参数 name;同样地,“nice to meet you”会被分配给第二个参数 msg。

当传递两个值时,函数运行地很顺利。但倘若参数的个数不匹配,会发生什么?

>>>greet('Pony')#少一个参数
...
TypeError:greet()missing1requiredpositionalargument:'msg'

显然,解释器会发牢骚。但对 Python 来说,要解决这个问题简直是易如反掌,继续往下看!

默认参数

在定义函数时,可以使用赋值运算符(=)为参数指定默认值:

>>>defgreet(name,msg='nicetomeetyou'):
...print('Hello,{},{}!'.format(name,msg))
...
>>>

注意: 如果参数没有默认值,在调用函数时必需为其指定一个值;如果有默认值,那么在调用时值是可选的,如果为其提供了一个值,将会覆盖默认值。

由于 name 没有默认值,所以必须指定值,而 msg 有默认值,所以值是可选的:

>>>greet('Pony')#使用默认值
Hello,Pony,nicetomeetyou!
>>>
>>>greet('Pony','givemeahug')#覆盖默认值
Hello,Pony,givemeahug!

值得注意的是,函数中的所有参数都可以有默认值,但是一旦存在一个默认参数,其右侧的所有参数也必须有默认值。

也就是说,非默认参数不能在默认参数之后。例如,像下面这样,就会报错:

>>>defgreet(msg='nicetomeetyou',name):
...print('Hello,{},{}!'.format(name,msg))
...
File"<stdin>",line1
SyntaxError:non-defaultargumentfollowsdefaultargument

关键字参数

为方便起见,Python 还允许使用 key = value 形式的关键字参数调用函数:

>>>defgreet(name,msg):
...print('Hello,{},{}!'.format(name,msg))
...>>>

当以这种方式调用函数时,所有传递的关键字参数都必须与函数接受的某个参数匹配,并且它们的顺序不重要:

>>>greet(name='Pony',msg='nicetomeetyou')#按循序传递
Hello,Pony,nicetomeetyou!
>>>
>>>greet(msg='nicetomeetyou',name='Pony')#顺序颠倒也可以
Hello,Pony,nicetomeetyou!

此外,它还可以和位置参数混合使用,但关键字参数必须在位置参数之后:

>>>greet('Pony',msg='nicetomeetyou')#位置参数与关键字参数混合使用
Hello,Pony,nicetomeetyou!

所以,如果像下面这样调用,就会报错:

>>>greet(name='Pony',message='nicetomeetyou')#message不存在
...
TypeError:greet()gotanunexpectedkeywordargument'message'
>>>
>>>greet(msg='nicetomeetyou','Pony')#关键字参数不能在位置参数之前
...
SyntaxError:positionalargumentfollowskeywordargument

可变参数

可变参数也被称为不定长参数,顾名思义,就是传入的参数个数是可变的,可以是任意多个(0、1、2 ...)。``

包裹位置传递

在参数名之前可以添加一个星号(*),在函数内部,所有传入的参数都会被变量 names 收集,最终按照位置将它们合并为一个元组:

>>>defgreet(*names):
...print('Hello,',names)
...
>>>

尝试一下,传递不同个数的参数:

>>>greet()#没有参数,为空元组
Hello,()
>>>
>>>greet('Pony')
Hello,('Pony',)
>>>
>>>greet('JackMa','Pony')
Hello,('JackMa','Pony')

通常情况下,可变参数(*)会出现在形参列表的最后,因为它们会把传递给函数的所有剩余输入参数都收集起来:

>>>defgreet(msg,*names):
...print('Hello,{},{}!'.format(names,msg))
...
>>>
>>>greet('nicetomeetyou','JackMa','Pony')
Hello,('JackMa','Pony'),nicetomeetyou!

话虽如此,但可变参数(*)之后还可以出现其它参数,只不过这些形参都是“强制关键字”参数,这意味着,它们只能被用作关键字参数,而不能是位置参数:

>>>defgreet(*names,msg):
...print('Hello,{},{}!'.format(names,msg))
...
>>>
>>>greet('Pony',msg='nicetomeetyou')#只能被用作关键字参数
Hello,('Pony',),nicetomeetyou!
>>>
>>>greet(msg='nicetomeetyou','Pony')#不能被用作位置参数
...
SyntaxError:positionalargumentfollowskeywordargument

包裹关键字传递

还有一种机制,在参数名之前添加两个星号(**),当这种形式出现时,msgs 将收集所有关键字参数,最终将它们合并为一个字典:

>>>defgreet(**msgs):
...print('Hello,',msgs)
...>>>

和上面一样,尝试传递不同个数的参数:

>>>greet()#没有参数,为空字典
Hello,{}
>>>
>>>greet(name='Pony')
Hello,{'name':'Pony'}
>>>
>>>greet(name='Pony',msg='nicetomeetyou')
Hello,{'name':'Pony','msg':'nicetomeetyou'}

此外,*names 还可以与 **msgs 形式的参数相结合,但*names 必须出现在 **msgs 之前。

例如,像下面这样,就会报错:

>>>defgreet(**msgs,*name):
...
SyntaxError:invalidsyntax

解包裹参数

正如【可变参数】那样,也可在函数调用中使用 * 和 **。只不过在这种情况下,与在函数定义中的语义相反,参数将被解包裹而不是被包裹:

>>>defgreet(name,msg):
...print('Hello,{},{}!'.format(name,msg))
...
>>>

尝试一下,元组用 * 来传递位置参数:

>>>t=('Pony','nicetomeetyou')
>>>greet(*t)
Hello,Pony,nicetomeetyou!

同样地,字典也可以用 ** 传递关键字参数:``

>>>d={'name':'Pony','msg':'nicetomeetyou'}
>>>greet(**d)
Hello,Pony,nicetomeetyou!

位置参数、默认参数、可变参数混合使用

根据上面的介绍,我们不难发现,当这些参数混合使用时,应遵循一个基本的原则:位置参数 -> 默认参数 -> 包裹位置 -> 包裹关键字(定义和调用都应遵循该顺序)。

例如,定义一个函数,能够同时向多个人发送多条消息:

>>>defgreet(sender,address='BeiJing',*receivers,**msgs):
...print('Hello,Iam{},from{}.'.format(sender,address))
...print('-'*30)#华丽的分割线
...forrecinreceivers:
...forkey,valinmsgs.items():
...print('No{}:{},{}!'.format(key,val,rec))
...print('-'*30)#华丽的分割线
...
>>>

为了显示更好的效果,我们在中间穿插了一些华丽的分割线:

>>>greet('Waleon','Xi\'an','JackMa','Pony',one='nicetomeetyou',two='givemeahug')
Hello,IamWaleon,fromXi'an.
------------------------------
Noone:nicetomeetyou,JackMa!
Notwo:givemeahug,JackMa!
------------------------------
Noone:nicetomeetyou,Pony!
Notwo:givemeahug,Pony!
------------------------------
THE END

发表回复