Python基礎(chǔ)-函數(shù)參數(shù)
寫(xiě)在前面
如非特別說(shuō)明灾常,下文均基于Python3
摘要
本文詳細(xì)介紹了函數(shù)的各種形參類(lèi)型俺叭,包括位置參數(shù)舌仍,默認(rèn)參數(shù)值妒貌,關(guān)鍵字參數(shù),任意參數(shù)列表铸豁,強(qiáng)制關(guān)鍵字參數(shù)灌曙;也介紹了調(diào)用函數(shù)時(shí)傳遞實(shí)參的各種方式,包括位置實(shí)參节芥,關(guān)鍵字實(shí)參以及使用*和**來(lái)解包序列和字典在刺。
1. 概述
函數(shù)在一定程度上是為了重用而創(chuàng)建的逆害。如果有一段非常優(yōu)秀的代碼段,實(shí)現(xiàn)了網(wǎng)絡(luò)資源下載的功能蚣驼,如果沒(méi)有函數(shù)魄幕,將會(huì)在每次需要實(shí)現(xiàn)網(wǎng)絡(luò)資源下載的地方復(fù)制該段代碼。懶惰即美德颖杏,將這段代碼抽象為函數(shù)纯陨,在需要使用的地方調(diào)用即可。
函數(shù)的使用有以下好處:
- 增加代碼的可讀性输玷。如在需要下載網(wǎng)絡(luò)資源的地方調(diào)用函數(shù):
download()
队丝,可以通過(guò)名字讀懂程序的目的; - 增加代碼可重用性欲鹏。相比復(fù)制大段代碼机久,調(diào)用函數(shù)的可操作性無(wú)疑更強(qiáng);
- 增加可維護(hù)性赔嚎。如果需要更改下載網(wǎng)絡(luò)資源的實(shí)現(xiàn)膘盖,沒(méi)有使用函數(shù)的情況下,不得不在每個(gè)實(shí)用下載功能的地方修改尤误,使用了函數(shù)侠畔,只需要修改函數(shù)即可;
- 減少犯錯(cuò)誤的可能性损晤。在復(fù)制代碼的過(guò)程中软棺,無(wú)疑會(huì)因?yàn)楦鞣N原因出現(xiàn)一些差錯(cuò),而函數(shù)不會(huì)尤勋。
函數(shù)定義非常簡(jiǎn)單:
def func([formal_parameter1, ... formal_parameter1]):
statement
以上函數(shù)定義的作用是創(chuàng)建函數(shù)對(duì)象喘落,并且在當(dāng)前作用域創(chuàng)建名字func
,指向函數(shù)對(duì)象最冰,在可及該作用域范圍內(nèi)瘦棋,可以使用名字func
調(diào)用函數(shù)。定義函數(shù)時(shí)候參數(shù)列表中的名字是函數(shù)形參暖哨,調(diào)用函數(shù)用的參數(shù)是實(shí)參赌朋。
Python
函數(shù)的參數(shù)十分強(qiáng)大,但相應(yīng)也為這種強(qiáng)大付出了相對(duì)復(fù)雜的代價(jià)篇裁。
函數(shù)定義時(shí)沛慢,函數(shù)的形參可以有以下幾種類(lèi)型:
- 位置參數(shù) positional parameters,最常用的形參形式达布,位置比名字重要团甲;
- 默認(rèn)參數(shù)值 default argument values,param_name = argu_value形式往枣,為形參提供默認(rèn)值伐庭,必須放置在位置參數(shù)之后;
- 任意參數(shù)列表 arbitrary argument lists分冈,*args形式圾另,args以元組的形式接收未匹配的位置實(shí)參;
- 關(guān)鍵字形參字典 keyword arguments雕沉, **kwargs形式集乔,kwargs以字典的形式接收未匹配的關(guān)鍵字實(shí)參,關(guān)鍵字參數(shù)需在任意參數(shù)列表之后坡椒;
- 強(qiáng)制關(guān)鍵字參數(shù) keyword-only arguments扰路,在任意參數(shù)列表之后(或者在單獨(dú)的*之后),調(diào)用是只能使用關(guān)鍵字實(shí)參倔叼。
函數(shù)調(diào)用時(shí)汗唱,實(shí)參可以由以下方式傳遞:
- 位置實(shí)參按照位置從左到右匹配,位置比名字重要丈攒;
- 關(guān)鍵字實(shí)參哩罪,通過(guò)明確形參的名字為其指定實(shí)參值。調(diào)用時(shí)關(guān)鍵字實(shí)參必須在位置實(shí)參之后巡验,且形參列表中要有與之匹配的關(guān)鍵字形參际插;
- 解包列表/字典,使用*(sequence)從序列中解包位置實(shí)參显设,使用**(dict)的方式從字典中解包關(guān)鍵字實(shí)參框弛。
當(dāng)這些不同的形參組合在一起時(shí),構(gòu)成的函數(shù)參數(shù)列表將會(huì)相當(dāng)復(fù)雜捕捂,始終牢記實(shí)參形參匹配是位置參數(shù)優(yōu)先瑟枫。而且,任意參數(shù)列表與關(guān)鍵字參數(shù)組合的形參列表绞蹦,可以匹配任意方式的函數(shù)調(diào)用力奋。
2. 位置參數(shù)
位置形參是最常見(jiàn)的形參類(lèi)型,其中幽七,位置比名字重要景殷,因?yàn)樵趯?shí)參匹配是是按照位置來(lái)的:
# positional argument, name is not important, but order matters
def positional_argument(name, age):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
調(diào)用時(shí),如果改變實(shí)參位置澡屡,意義完全不同:
positional_argument('Richard', 20)
positional_argument(20, 'Richard')
位置形參和位置實(shí)參(統(tǒng)稱(chēng)位置參數(shù))是最重要的參數(shù)類(lèi)型猿挚,在參數(shù)匹配中它的優(yōu)先級(jí)是最高的。
3. 參數(shù)默認(rèn)值
有其他高級(jí)語(yǔ)言(如java)經(jīng)驗(yàn)的人知道驶鹉,有重載函數(shù)這一說(shuō)法绩蜻,兩個(gè)函數(shù)的名字相同,其參數(shù)列表不同室埋,功能不同办绝。調(diào)用者通過(guò)指定不同的實(shí)參伊约,調(diào)用不同形參的重載函數(shù)。
但是在Python中沒(méi)有重載函數(shù)的說(shuō)法孕蝉,因?yàn)槟J(rèn)參數(shù)值得存在屡律,是的調(diào)用者在調(diào)用同一個(gè)函數(shù)的時(shí)候可以指定不同參數(shù)。雖然不支持重載降淮,但是Python以默認(rèn)參數(shù)值的方式實(shí)現(xiàn)了重載函數(shù)的功能超埋。
指定了默認(rèn)參數(shù)值的形參不能位于位置參數(shù)之前,因?yàn)閷?shí)參匹配是位置優(yōu)先的佳鳖,這時(shí)在前面的指定了默認(rèn)值的參數(shù)會(huì)被位置實(shí)參覆蓋霍殴,導(dǎo)致后面的位置形參無(wú)法匹配到實(shí)參值而調(diào)用失敗:
# default argument values, non-default argument cann't follow default argument
def default_argument_value(name, age = 20, id = '0001'):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
print('id->type:%s, value:%s' % (type(id), id))
# 調(diào)用時(shí)系吩,可以有多種實(shí)參形式
# 指定唯一的強(qiáng)制參數(shù)
default_argument_value('Richard')
# 指定其中一個(gè)默認(rèn)參數(shù)
default_argument_value('Richard', 22)
# 指定全部參數(shù)
default_argument_value('Richard', 22, '002')
4. 任意參數(shù)列表
Python的函數(shù)相較于其他高級(jí)語(yǔ)言強(qiáng)大的地方在于来庭,可以收集多余的未匹配到形參的實(shí)參。使用如下格式的形參:*args
穿挨,收集到尚未匹配到形參的實(shí)際參數(shù)巾腕。
接收的額外位置實(shí)參以元組的形式存儲(chǔ),且任意參數(shù)列表需要在位置參數(shù)之后:
# Arbitrary Argument Lists
# It receives a tuple containing the positional arguments beyond the formal parameter list. (*name must occur before **name.)
# be last in the list of formal parameters, because they scoop up all remaining input arguments that are passed to the function
def arbitrary_arguments_list(name, age, *args):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
print('args->type:%s, value:%s' % (type(args), args))
# 實(shí)參1, 2, 3沒(méi)有位置形參匹配絮蒿,被任意參數(shù)列表收集
arbitrary_arguments_list('Richard', 20, 1, 2, 3)
output:
name->type:<class 'str'>, value:Richard
age->type:<class 'int'>, value:20
args->type:<class 'tuple'>, value:(1, 2, 3)
5. 關(guān)鍵字參數(shù)
在調(diào)用函數(shù)時(shí)尊搬,通過(guò)位置參數(shù)方式調(diào)用,每個(gè)參數(shù)到底匹配哪個(gè)形參是不容易發(fā)現(xiàn)的土涝,之后查看函數(shù)定義才能知道佛寿。可以通過(guò)指定形參對(duì)應(yīng)的實(shí)參值的方式調(diào)用但壮,這樣實(shí)參形參的匹配更加明了冀泻。
還是以位置形參為例:
# positional argument, name is not important, but order matters
def positional_argument(name, age):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
在調(diào)用時(shí)可以通過(guò)關(guān)鍵字方式:
# keyword arguments.
# In a function call, keyword arguments must follow positional arguments.
# All the keyword arguments passed must match one of the arguments accepted by the function, and their order is not important.
positional_argument(age = 20, name = 'Richard')
關(guān)鍵字實(shí)參必須在位置實(shí)參之后,并且可以在形參列表中匹配到形參名字蜡饵,否則調(diào)用失數妗:
# 形參中沒(méi)有名為id的參數(shù),所以調(diào)用失敗
positional_argument(age = 20, name = 'Richard', id = '003')
收集多余關(guān)鍵字實(shí)參
任意參數(shù)列表能夠接收沒(méi)有匹配到位置形參的實(shí)參溯祸,而關(guān)鍵字形參字典能夠接受為匹配到關(guān)鍵字參數(shù)的實(shí)參肢专。通過(guò)如**kwargs
的方式,收集尚未匹配的關(guān)鍵字實(shí)參焦辅,關(guān)鍵字參數(shù)字典也要在位置參數(shù)之后:
# keyword arguments dict **kwargs.
# It receives a dictionary containing all keyword arguments except for those corresponding to a formal parameter.
def keyword_argument_dict(name, age, **kwargs):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
print('kwargs->type:%s, value:%s' % (type(kwargs), kwargs))
keyword_argument_dict(name = 'Richard', age = 20, id = '0001', type = 'it')
output:
name->type:<class 'str'>, value:Richard
age->type:<class 'int'>, value:20
kwargs->type:<class 'dict'>, value:{'id': '0001', 'type': 'it'}
另外博杖,關(guān)鍵字形參字典需要在任意參數(shù)列表之后。
6. 強(qiáng)制關(guān)鍵字參數(shù)
任意出現(xiàn)在*arg
或者*
之后的形參都是命名關(guān)鍵字參數(shù)筷登,意味著它們只能作為關(guān)鍵字實(shí)參匹配剃根,而非位置實(shí)參。
# Keyword only argument.
# Any formal parameters which occur after the *args parameter are ‘keyword-only’ arguments,
# meaning that they can only be used as keywords rather than positional arguments.
def keyword_only_argument(name, *, age, id):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
print('id->type:%s, value:%s' % (type(id), id))
keyword_only_argument('Richard', age = 20, id = '001')
output:
name->type:<class 'str'>, value:Richard
age->type:<class 'int'>, value:20
id->type:<class 'str'>, value:001
7. 序列和字典實(shí)參的解包
在函數(shù)調(diào)用時(shí)前方,使用*sequence
將序列解包為位置實(shí)參狈醉;
使用**dict
將字典解包為關(guān)鍵字實(shí)參廉油。
def mix_param(name, *args, **kwargs):
print('name->type:%s, value:%s' % (type(name), name))
print('args->type:%s, value:%s' % (type(args), args))
print('kwargs->type:%s, value:%s' % (type(kwargs), kwargs))
mix_param('Richard', *(1, 2, 3), **{'age':20, 'id':'001'})
output:
name->type:<class 'str'>, value:Richard
args->type:<class 'tuple'>, value:(1, 2, 3)
kwargs->type:<class 'dict'>, value:{'age': 20, 'id': '001'}
注意到*args
解包為位置參數(shù),而**kwargs
解包為關(guān)鍵字參數(shù)苗傅,涵蓋了Python中所有可能出現(xiàn)的實(shí)參類(lèi)型娱两。因此,可以使用這兩個(gè)組合調(diào)用任意形參實(shí)行的函數(shù):
def foo(x, y, z, m = 0, n = 0):
print(x, y, z, m, n)
def call_foo(*args, **kwargs):
print('Call foo!')
foo(*args, **kwargs)
注意到call_foo
函數(shù)中金吗,args
是一個(gè)元組,kwargs
是一個(gè)字典趣竣,所以可以解包他們組合調(diào)用任意形參形式的函數(shù)摇庙。這一種方式在調(diào)用父類(lèi)構(gòu)造函數(shù)時(shí)非常有用!