函數(shù)調(diào)用時(shí)所提供的參數(shù)可以是?
一 形參與實(shí)參介紹
函數(shù)的參數(shù)分為形式參數(shù)和實(shí)際參數(shù),簡(jiǎn)稱(chēng)形參和實(shí)參:
形參即在定義函數(shù)時(shí),括號(hào)內(nèi)聲明的參數(shù)。形參本質(zhì)就是一個(gè)變量名,用來(lái)接收外部傳來(lái)的值。
實(shí)參即在調(diào)用函數(shù)時(shí),括號(hào)內(nèi)傳入的值,值可以是常量、變量、表達(dá)式或三者的組合:
1:實(shí)參是常量
res=my_min(1,2)
2:實(shí)參是變量
a=1
b=2
res=my_min(a,b)
3:實(shí)參是表達(dá)式
res=my_min(102,10my_min(3,4))
4:實(shí)參可以是常量、變量、表達(dá)式的任意組合
a=2
my_min(1,a,10*my_min(3,4))
在調(diào)用有參函數(shù)時(shí),實(shí)參(值)會(huì)賦值給形參(變量名)。在Python中,變量名與值只是單純的綁定關(guān)系,而對(duì)于函數(shù)來(lái)說(shuō),這種綁定關(guān)系只在函數(shù)調(diào)用時(shí)生效,在調(diào)用結(jié)束后解除。
二 形參與實(shí)參的具體使用
2.1 位置參數(shù)
位置即順序,位置參數(shù)指的是按順序定義的參數(shù),需要從兩個(gè)角度去看:
在定義函數(shù)時(shí),按照從左到右的順序依次定義形參,稱(chēng)為位置形參,凡是按照這種形式定義的形參都必須被傳值
def register(name,age,sex): #定義位置形參:name,age,sex,三者都必須被傳值
print('Name:%s Age:%s Sex:%s' %(name,age,sex))
register() #TypeError:缺少3個(gè)位置參數(shù)
在調(diào)用函數(shù)時(shí),按照從左到右的順序依次定義實(shí)參,稱(chēng)為位置實(shí)參,凡是按照這種形式定義的實(shí)參會(huì)按照從左到右的順序與形參一一對(duì)應(yīng)
def register(name,age,sex): #定義位置形參:name,age,sex,三者都必須被傳值
print('Name:%s Age:%s Sex:%s' %(name,age,sex))
register() #TypeError:缺少3個(gè)位置參數(shù)
2.2 關(guān)鍵字參數(shù)
在調(diào)用函數(shù)時(shí),實(shí)參可以是key=value的形式,稱(chēng)為關(guān)鍵字參數(shù),凡是按照這種形式定義的實(shí)參,可以完全不按照從左到右的順序定義,但仍能為指定的形參賦值
register(sex='male',name='lili',age=18)
Name:lili Age:18 Sex:male
需要注意在調(diào)用函數(shù)時(shí),實(shí)參也可以是按位置或按關(guān)鍵字的混合使用,但必須保證關(guān)鍵字參數(shù)在位置參數(shù)后面,且不可以對(duì)一個(gè)形參重復(fù)賦值
register('lili',sex='male',age=18) #正確使用
register(name='lili',18,sex='male') #SyntaxError:關(guān)鍵字參數(shù)name=‘lili’在位置參數(shù)18之前
register('lili',sex='male',age=18,name='jack') #TypeError:形參name被重復(fù)賦值
2.3 默認(rèn)參數(shù)
在定義函數(shù)時(shí),就已經(jīng)為形參賦值,這類(lèi)形參稱(chēng)之為默認(rèn)參數(shù),當(dāng)函數(shù)有多個(gè)參數(shù)時(shí),需要將值經(jīng)常改變的參數(shù)定義成位置參數(shù),而將值改變較少的參數(shù)定義成默認(rèn)參數(shù)。例如編寫(xiě)一個(gè)注冊(cè)學(xué)生信息的函數(shù),如果大多數(shù)學(xué)生的性別都為男,那完全可以將形參sex定義成默認(rèn)參數(shù)
def register(name,age,sex='male'): #默認(rèn)sex的值為male
... print('Name:%s Age:%s Sex:%s' %(name,age,sex))
...
定義時(shí)就已經(jīng)為參數(shù)sex賦值,意味著調(diào)用時(shí)可以不對(duì)sex賦值,這降低了函數(shù)調(diào)用的復(fù)雜度
register('tom',17) #大多數(shù)情況,無(wú)需為sex傳值,默認(rèn)為male
Name:tom Age:17 Sex:male
register('Lili',18,'female') #少數(shù)情況,可以為sex傳值female
Name:Lili Age:18 Sex:female
需要注意:
默認(rèn)參數(shù)必須在位置參數(shù)之后
默認(rèn)參數(shù)的值僅在函數(shù)定義階段被賦值一次
x=1
def foo(arg=x):
... print(arg)
...
x=5 #定義階段arg已被賦值為1,此處的修改與默認(rèn)參數(shù)arg無(wú)任何關(guān)系
foo()
1
默認(rèn)參數(shù)的值通常應(yīng)設(shè)為不可變類(lèi)型
def foo(n,arg=[]):
arg.append(n)
return arg
foo(1)
[1]
foo(2)
[1, 2]
foo(3)
[1, 2, 3]
每次調(diào)用是在上一次的基礎(chǔ)上向同一列表增加值,修改如下
def foo(n,arg=None):
if arg is None:
arg=[]
arg.append(n)
return arg
foo(1)
[1]
foo(2)
[2]
foo(3)
[3]
2.4 可變長(zhǎng)度的參數(shù)(與*的用法)
參數(shù)的長(zhǎng)度可變指的是在調(diào)用函數(shù)時(shí),實(shí)參的個(gè)數(shù)可以不固定,而在調(diào)用函數(shù)時(shí),實(shí)參的定義無(wú)非是按位置或者按關(guān)鍵字兩種形式,這就要求形參提供兩種解決方案來(lái)分別處理兩種形式的可變長(zhǎng)度的參數(shù)
2.4.1 可變長(zhǎng)度的位置參數(shù)
如果在最后一個(gè)形參名前加號(hào),那么在調(diào)用函數(shù)時(shí),溢出的位置實(shí)參,都會(huì)被接收,以元組的形式保存下來(lái)賦值給該形參
def foo(x,y,z=1,Args): #在最后一個(gè)形參名args前加號(hào)
... print(x)
... print(y)
... print(z)
... print(args)
...
foo(1,2,3,4,5,6,7) #實(shí)參1、2、3按位置為形參x、y、z賦值,多余的位置實(shí)參4、5、6、7都被*接收,以元組的形式保存下來(lái),賦值給args,即args=(4, 5, 6,7)
1
2
3
(4, 5, 6, 7)
如果我們事先生成了一個(gè)列表,仍然是可以傳值給*args的
def foo(x,y,*args):
... print(x)
... print(y)
... print(args)
...
L=[3,4,5]
foo(1,2,*L) # L就相當(dāng)于位置參數(shù)3,4,5, foo(1,2,L)就等同于foo(1,2,3,4,5)
1
2
(3, 4, 5)
注意:如果在傳入L時(shí)沒(méi)有加*,那L就只是一個(gè)普通的位置參數(shù)了
foo(1,2,L) #僅多出一個(gè)位置實(shí)參L
1
2
([1, 2, 3],)
如果形參為常規(guī)的參數(shù)(位置或默認(rèn)),實(shí)參仍可以是*的形式
def foo(x,y,z=3):
... print(x)
... print(y)
... print(z)
...
foo([1,2]) #等同于foo(1,2)
1
2
3
如果我們想要求多個(gè)值的和,args就派上用場(chǎng)了
def add(*args):
... res=0
... for i in args:
... res+=i
... return res
...
add(1,2,3,4,5)
15
2.4.2 可變長(zhǎng)度的關(guān)鍵字參數(shù)
如果在最后一個(gè)形參名前加號(hào),那么在調(diào)用函數(shù)時(shí),溢出的關(guān)鍵字參數(shù),都會(huì)被接收,以字典的形式保存下來(lái)賦值給該形參
def foo(x,kwargs): #在最后一個(gè)參數(shù)kwargs前加
... print(x)
... print(kwargs)
...
foo(y=2,x=1,z=3) #溢出的關(guān)鍵字實(shí)參y=2,z=3都被接收,以字典的形式保存下來(lái),賦值給kwargs
1
{'z': 3, 'y': 2}
如果我們事先生成了一個(gè)字典,仍然是可以傳值給kwargs的
def foo(x,y,**kwargs):
... print(x)
... print(y)
... print(kwargs)
...
dic={'a':1,'b':2}
foo(1,2,dic) #dic就相當(dāng)于關(guān)鍵字參數(shù)a=1,b=2,foo(1,2,**dic)等同foo(1,2,a=1,b=2)
1
2
{'a': 1, 'b': 2}
注意:如果在傳入dic時(shí)沒(méi)有加**,那dic就只是一個(gè)普通的位置參數(shù)了
foo(1,2,dic) #TypeError:函數(shù)foo只需要2個(gè)位置參數(shù),但是傳了3個(gè)
如果形參為常規(guī)參數(shù)(位置或默認(rèn)),實(shí)參仍可以是**的形式
def foo(x,y,z=3):
... print(x)
... print(y)
... print(z)
...
foo({'x':1,'y':2}) #等同于foo(y=2,x=1)
1
2
3
如果我們要編寫(xiě)一個(gè)用戶(hù)認(rèn)證的函數(shù),起初可能只基于用戶(hù)名密碼的驗(yàn)證就可以了,可以使用kwargs為日后的擴(kuò)展供良好的環(huán)境,同時(shí)保持了函數(shù)的簡(jiǎn)潔性。
def auth(user,password,kwargs):
... pass
...
2.5 命名關(guān)鍵字參數(shù)
在定義了kwargs參數(shù)后,函數(shù)調(diào)用者就可以傳入任意的關(guān)鍵字參數(shù)key=value,如果函數(shù)體代碼的執(zhí)行需要依賴(lài)某個(gè)key,必須在函數(shù)內(nèi)進(jìn)行判斷
def register(name,age,**kwargs):
... if 'sex' in kwargs:
... #有sex參數(shù)
... pass
... if 'height' in kwargs:
... #有height參數(shù)
... pass
...
想要限定函數(shù)的調(diào)用者必須以key=value的形式傳值,Python3提供了專(zhuān)門(mén)的語(yǔ)法:需要在定義形參時(shí),用作為一個(gè)分隔符號(hào),號(hào)之后的形參稱(chēng)為命名關(guān)鍵字參數(shù)。對(duì)于這類(lèi)參數(shù),在函數(shù)調(diào)用時(shí),必須按照key=value的形式為其傳值,且必須被傳值
def register(name,age,*,sex,height): #sex,height為命名關(guān)鍵字參數(shù)
... pass
...
register('lili',18,sex='male',height='1.8m') #正確使用
register('lili',18,'male','1.8m') # TypeError:未使用關(guān)鍵字的形式為sex和height傳值
register('lili',18,height='1.8m') # TypeError沒(méi)有為命名關(guān)鍵字參數(shù)height傳值。
命名關(guān)鍵字參數(shù)也可以有默認(rèn)值,從而簡(jiǎn)化調(diào)用
def register(name,age,*,sex='male',height):
... print('Name:%s,Age:%s,Sex:%s,Height:%s' %(name,age,sex,height))
...
register('lili',18,height='1.8m')
Name:lili,Age:18,Sex:male,Height:1.8m
需要強(qiáng)調(diào)的是:sex不是默認(rèn)參數(shù),height也不是位置參數(shù),因?yàn)槎呔诤螅远际敲P(guān)鍵字參數(shù),形參sex=’male’屬于命名關(guān)鍵字參數(shù)的默認(rèn)值,因而即便是放到形參height之前也不會(huì)有問(wèn)題。另外,如果形參中已經(jīng)有一個(gè)args了,命名關(guān)鍵字參數(shù)就不再需要一個(gè)單獨(dú)的*作為分隔符號(hào)了
def register(name,age,*args,sex='male',height):
... print('Name:%s,Age:%s,Args:%s,Sex:%s,Height:%s' %(name,age,args,sex,height))
...
register('lili',18,1,2,3,height='1.8m') #sex與height仍為命名關(guān)鍵字參數(shù)
Name:lili,Age:18,Args:(1, 2, 3),Sex:male,Height:1.8m
2.6 組合使用
綜上所述所有參數(shù)可任意組合使用,但定義順序必須是:位置參數(shù)、默認(rèn)參數(shù)、args、命名關(guān)鍵字參數(shù)、kwargs
可變參數(shù)args與關(guān)鍵字參數(shù)kwargs通常是組合在一起使用的,如果一個(gè)函數(shù)的形參為*args與kwargs,那么代表該函數(shù)可以接收任何形式、任意長(zhǎng)度的參數(shù)
def wrapper(*args,**kwargs):
... pass
...
在該函數(shù)內(nèi)部還可以把接收到的參數(shù)傳給另外一個(gè)函數(shù)(這在4.6小節(jié)裝飾器的實(shí)現(xiàn)中大有用處)
def func(x,y,z):
... print(x,y,z)
...
def wrapper(args,**kwargs):
... func(args,**kwargs)
...
wrapper(1,z=3,y=2)
1 2 3
按照上述寫(xiě)法,在為函數(shù)wrapper傳參時(shí),其實(shí)遵循的是函數(shù)func的參數(shù)規(guī)則,調(diào)用函數(shù)wrapper的過(guò)程分析如下:
位置實(shí)參1被接收,以元組的形式保存下來(lái),賦值給args,即args=(1,),關(guān)鍵字實(shí)參z=3,y=2被接收,以字典的形式保存下來(lái),賦值給kwargs,即kwargs={'y': 2, 'z': 3}
執(zhí)行func(args,kwargs),即func((1,), {'y': 2, 'z': 3}),等同于func(1,z=3,y=2)
提示: *args、**kwargs中的args和kwargs被替換成其他名字并無(wú)語(yǔ)法錯(cuò)誤,但使用args、kwargs是約定俗成的。