位置參數(shù)
我們?cè)诶煤瘮?shù)計(jì)算一個(gè)數(shù)的平方時(shí),必須要給這個(gè)函數(shù)傳入一個(gè)參數(shù)——即我們要計(jì)算平方的數(shù)。
def pow(x):
return x * x
對(duì)于函數(shù)pow來(lái)說(shuō),x就是一個(gè)位置參數(shù),當(dāng)調(diào)用這個(gè)函數(shù)時(shí),x是必須傳入有且唯一的一個(gè)參數(shù)。
當(dāng)我們想計(jì)算 x3,可以定義另一個(gè)函數(shù),那求x4呢,還要再定義嗎,其實(shí)我們只需對(duì)pow函數(shù)稍加改動(dòng)即可
def pow(x, n):
prod = 1
while n > 0:
prod = prod * x
n = n-1
return prod
這時(shí),x 和 n 就都是位置參數(shù),在調(diào)用時(shí)必須同時(shí)傳入,按照順序依次賦值給x 和 n。
默認(rèn)參數(shù)
在實(shí)際應(yīng)用中我們必須考慮這種情況:一般求冪都是求平方,每次使用pow函數(shù)都要輸入兩個(gè)參數(shù)未免太麻煩了些,所以有了默認(rèn)參數(shù),即這個(gè)參數(shù)有個(gè)缺省值,在不輸入是默認(rèn)為缺省值。
【例1】:
def pow(x, n = 2):
prod = 1
while n > 0:
prod = prod * x
n = n-1
return prod
當(dāng)把n由變成n=2時(shí),位置參數(shù)就變成了默認(rèn)參數(shù),當(dāng)我們只傳入一個(gè)參數(shù)時(shí),n默認(rèn)為2。即pow(3)計(jì)算的是32。
對(duì)于其他n>2的情況,就必須要傳入兩個(gè)參數(shù)了。
使用默認(rèn)參數(shù)有以下情況需要注意:
函數(shù)的參數(shù)可以全由默認(rèn)參數(shù)組成
但當(dāng)位置參數(shù)和默認(rèn)參數(shù)同時(shí)存在時(shí),默認(rèn)參數(shù)必須定義在位置參數(shù)的后面,否則會(huì)報(bào)錯(cuò)
如何設(shè)置默認(rèn)參數(shù):
一般把變化大的設(shè)為位置參數(shù),變化小的(默認(rèn)值使用次數(shù)占多)設(shè)為默認(rèn)參數(shù),如【例1】 的pow函數(shù)
當(dāng)有多個(gè)默認(rèn)參數(shù)時(shí),可以不按照默認(rèn)參數(shù)的定義順序給出,但必須同時(shí)給出參數(shù)名稱,如【例2】:
【例2】:
def enroll(name, gender, age = 6, city = 'Beijing', district = 'Chaoyang'):
print('name:', name)
print('gender:', gender)
print('age:', age)
print('city:', city)
print('district:', district)
我們可以這樣調(diào)用:enroll('xiaoming', 'M', district = 'Jizhou', city = 'Tianjin')
運(yùn)行結(jié)果:
name: xiaoming
gender: M
age: 6
city: Tianjin
district: Jizhou
重點(diǎn):默認(rèn)參數(shù)必須指向不變對(duì)象
【例3】:
def add_end(L = []):
L.append('end')
return L
當(dāng)我們第一次運(yùn)行該函數(shù)時(shí),結(jié)果正常:
>>> add_end()
['end']
但當(dāng)我們?cè)龠\(yùn)行時(shí),會(huì)發(fā)現(xiàn):
>>> add_end()
['end', 'end']
>>> add_end()
['end', 'end', 'end']
每次運(yùn)行都會(huì)記錄這個(gè)添加的end結(jié)尾,但這并非我們的本意,究竟為何如此呢?
我們定義的默認(rèn)參數(shù)L是個(gè)變量,其指向?yàn)?[] 這個(gè)list,但運(yùn)行一次之后,其指向的list內(nèi)容發(fā)生了改變,已經(jīng)變成['end'],默認(rèn)參數(shù)的內(nèi)容就變了,不再是函數(shù)定義時(shí)的 [] 了。
所以:
定義默認(rèn)參數(shù)要牢記一點(diǎn):默認(rèn)參數(shù)必須指向不變對(duì)象!
有哪些數(shù)據(jù)類型是不變對(duì)象呢,比如:str,None,不變對(duì)象一旦創(chuàng)建,其內(nèi)部數(shù)據(jù)便不可更改,減少了由于修改數(shù)據(jù)導(dǎo)致的錯(cuò)誤。
可變參數(shù)
可變參數(shù),顧名思義,就是傳入?yún)?shù)的數(shù)量可以變化,可以是1個(gè),2個(gè),3個(gè)……
【例4】:
def cal(numbers):
sum = 0
for x in numbers:
sum = sum + x
return sum
由此我們可以計(jì)算1+2+4+5+8+...的值了,但每次調(diào)用我們傳入的numbers參數(shù)必須是一個(gè) list 或 tuple,較麻煩。此時(shí)的numbers是一個(gè)位置參數(shù),不是可變參數(shù)。
稍加改動(dòng),即可將 numbers 定義為一個(gè)可變參數(shù)
【例4.1】
def cal(*numbers):
sum = 0
for x in numbers:
sum = sum + x
return sum
只是在numbers前加了個(gè)*,numbers就成為了可變參數(shù),我們?cè)谡{(diào)用此函數(shù)時(shí)可以這樣調(diào)用:
cal(1,2,3,6,7,8),此時(shí)numbers接收到的是一個(gè)tuple,函數(shù)代碼不變,函數(shù)即可傳入任意個(gè)參數(shù),包括0個(gè)參數(shù)。
>>> cal(3,56,7,8)
74
>>> cal()
0
如果numbers是位置參數(shù)時(shí)則0個(gè)參數(shù)會(huì)報(bào)錯(cuò),因?yàn)槲恢脜?shù)是必須傳入的。
當(dāng)我已經(jīng)有一個(gè) list 或 tuple 了怎么把它傳入函數(shù)中,可以這樣做:
>>> num = [1,3,5,6,7,8]
>>> cal(*num)
30
在 list 或 tuple 前加一個(gè)*就能將其傳入函數(shù)中了。
關(guān)鍵字參數(shù)
可變參數(shù)是可以傳入任意個(gè)(包括0個(gè))參數(shù),這些可變參數(shù)在傳入時(shí)會(huì)自動(dòng)組裝成一個(gè)tuple。關(guān)鍵字參數(shù)是允許你傳入任意個(gè)(包括0個(gè))含參數(shù)名的參數(shù),這些關(guān)鍵字參數(shù)會(huì)自動(dòng)組裝成為一個(gè)dict
【例5】:
def person(name, age, **kw):
print('name:', name, 'age:', age, kw)
函數(shù)person在調(diào)用時(shí)除了接受必選參數(shù)name和age,還接受關(guān)鍵字參數(shù)kw。調(diào)用時(shí)可以只傳入位置參數(shù)
>>> person('xiaoming','6')
name: xiaoming age: 6 {}
也可以傳入任意個(gè)數(shù)的關(guān)鍵字參數(shù):
>>> person('xiaoming','6',city = 'Beijing', job = 'Engineer')
name: xiaoming age: 6 {'city': 'Beijing', 'job': 'Engineer'}
可以看到,兩個(gè)關(guān)鍵字參數(shù)已經(jīng)自動(dòng)組成為dict了。
應(yīng)用場(chǎng)景:
在用戶注冊(cè)時(shí),用戶可以只填入姓名,年齡兩個(gè)參數(shù),但提供城市,職業(yè)等參數(shù)選填。
與可變參數(shù)相同,我們也可以提前組裝好一個(gè)dict,之后利用**dict名將其傳入函數(shù):
>>> d = { 'city' : 'Beijing', 'job' : 'Engineer'}
>>> person('xiaoming', 6, **d)
name: xiaoming age: 6 {'city': 'Beijing', 'job': 'Engineer'}
**d會(huì)將此dict的所有key-value用關(guān)鍵字參數(shù)傳入到函數(shù)的**kw參數(shù)中,kw將獲得一個(gè)dict,其獲得的是d的一份拷貝,對(duì)**kw做任何改動(dòng)不會(huì)影響d。
命名關(guān)鍵字參數(shù)
關(guān)鍵字參數(shù)允許我們傳入任意個(gè)含參數(shù)名的關(guān)鍵字參數(shù),但是考慮到限制用戶輸入的隨意性,如何讓用戶只傳入規(guī)定的關(guān)鍵字參數(shù)呢。比如,只接受city和job作為關(guān)鍵字參數(shù),可以這樣定義:
def person(name, age, *, city, job):
print(name, age, city, job)
和關(guān)鍵字參數(shù)**kw不同,命名關(guān)鍵字參數(shù)只需要一個(gè)特殊分隔符*,*后面的參數(shù)都會(huì)被視為命名關(guān)鍵字參數(shù)。
調(diào)用方式如下:
>>> person('xiaoming',6,city = 'Beijing', job = 'Engineer')
xiaoming 6 Beijing Engineer
命名關(guān)鍵字參數(shù)調(diào)用時(shí)必須寫參數(shù)名,不寫的話函數(shù)會(huì)默認(rèn)將其視為位置參數(shù),將會(huì)報(bào)錯(cuò)。
如果命名關(guān)鍵字參數(shù)前面有可變參數(shù),則省略*作為分隔符,可變參數(shù)后的參數(shù)自動(dòng)視為命名關(guān)鍵字參數(shù),不必?fù)?dān)心混淆,命名關(guān)鍵字參數(shù)傳入時(shí)必須包含參數(shù)名。
def person(name, age, *args , city, job):
print(name, age,args, city, job)
>>> person('xiaoming',6, 'This is a smart boy', 'allergic to egg', city = 'Beijing', job = 'Engineer')
xiaoming 6 ('This is a smart boy', 'allergic to egg') Beijing Engineer
命名關(guān)鍵字參數(shù)也可以有缺省值,從而簡(jiǎn)化調(diào)用:
def person(name, age, *args , city = ‘Beijing, job):
print(name, age,args, city, job)
>>> person('xiaoming',6, 'This is a smart boy', 'allergic to egg', job = 'Engineer')
xiaoming 6 ('This is a smart boy', 'allergic to egg') Beijing Engineer
注意:
使用命名關(guān)鍵字參數(shù)時(shí),如果前面沒(méi)有可變參數(shù),必須加特殊分隔符*。如果不加,python將無(wú)法識(shí)別位置參數(shù)和命名關(guān)鍵字參數(shù)。
參數(shù)組合
這五種參數(shù)可以組合使用,但參數(shù)定義的順序必須是:
位置參數(shù)-->默認(rèn)參數(shù)-->可變參數(shù)-->命名關(guān)鍵字參數(shù)-->關(guān)鍵字參數(shù)
注意命名關(guān)鍵字參數(shù)和關(guān)鍵字參數(shù)的順序。
比如定義一個(gè)函數(shù),包含若干上述參數(shù):
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
可以看出,
f1包含位置參數(shù),默認(rèn)參數(shù),可變參數(shù),關(guān)鍵字參數(shù)。
f2包含位置參數(shù),默認(rèn)參數(shù),命名關(guān)鍵字參數(shù),關(guān)鍵字參數(shù)。
其中可變參數(shù)在傳入時(shí)自動(dòng)組成一個(gè)tuple,關(guān)鍵字參數(shù)傳入時(shí)自動(dòng)組成一個(gè)dict。
函數(shù)調(diào)用時(shí),python解釋器會(huì)自動(dòng)按參數(shù)位置和參數(shù)名把對(duì)應(yīng)的參數(shù)傳進(jìn)去。
f1可以只傳入位置參數(shù),f2必須傳入的有位置參數(shù),命名關(guān)鍵字參數(shù)。
>>> f1(1,2)
a = 1 b = 2 c = 0 args = () kw = {}
>>> f1(1,2,4)
a = 1 b = 2 c = 4 args = () kw = {}
>>> f1(1,2,c=4)
a = 1 b = 2 c = 4 args = () kw = {}
>>> f1(1,2,4,'ab',2)
a = 1 b = 2 c = 4 args = ('ab', 2) kw = {}
>>> f1(1,2,4,'ab',2,city = 'Beijing')
a = 1 b = 2 c = 4 args = ('ab', 2) kw = {'city': 'Beijing'}
>>> f2(1,2,4,d = 6)
a = 1 b = 2 c = 4 d = 6 kw = {}
>>> f2(1,2,4,d = 6,city = 'Beijing')
a = 1 b = 2 c = 4 d = 6 kw = {'city': 'Beijing'}
>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
最神奇的是,通過(guò)一個(gè)tuple和dict,你也可以調(diào)用上述函數(shù):
>>> args = (1,2,3,4)
>>> kw = {'d':99,'x':'#'}
>>> f1(*args,**kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>> kw = {'d':456,'city':'Beijing'}
>>> f2(*args,**kw)
a = 1 b = 2 c = 3 d = 456 kw = {'city': 'Beijing'}
無(wú)論函數(shù)是如何定義的,都可以通過(guò)類似func(*args,**kw)的方式調(diào)用它。
最后:
不要同時(shí)使用太多的組合,會(huì)使函數(shù)接口的可理解性很差