目錄
- 一 形參與實(shí)參介紹
-
二 形參與實(shí)參的具體使用
- 2.1 位置參數(shù)
- 2.2 關(guān)鍵字參數(shù)
- 2.3 默認(rèn)參數(shù)
- 2.4 可變長度的參數(shù) (與*用法)
- 2.4.1 可變長度的位置參數(shù)
- 2.4.2 可變長度的關(guān)鍵字參數(shù)
- 2.5 命名關(guān)鍵字參數(shù)
- 2.6 組合使用
- 三 視頻鏈接
一 形參與實(shí)參介紹
函數(shù)的參數(shù)分為形式參數(shù)和實(shí)際參數(shù),簡稱形參和實(shí)參:
形參即在定義函數(shù)時(shí),括號內(nèi)聲明的參數(shù)。形參本質(zhì)就是一個變量名,用來接收外部傳來的值。
實(shí)參即在調(diào)用函數(shù)時(shí),括號內(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(10*2,10*my_min(3,4))
#4:實(shí)參可以是常量、變量、表達(dá)式的任意組合
a=2
my_min(1,a,10*my_min(3,4))
在調(diào)用有參函數(shù)時(shí),實(shí)參(值)會賦值給形參(變量名)。在Python中,變量名與值只是單純的綁定關(guān)系,而對于函數(shù)來說,這種綁定關(guān)系只在函數(shù)調(diào)用時(shí)生效,在調(diào)用結(jié)束后解除。
二 形參與實(shí)參的具體使用
2.1 位置參數(shù)
位置即順序,位置參數(shù)指的是按順序定義的參數(shù),需要從兩個角度去看:
-
在定義函數(shù)時(shí),按照從左到右的順序依次定義形參,稱為位置形參,凡是按照這種形式定義的形參都必須被傳值
>>> def register(name,age,sex): #定義位置形參:name,age,sex,三者都必須被傳值 ... print('Name:%s Age:%s Sex:%s' %(name,age,sex)) ... >>> register() #TypeError:缺少3個位置參數(shù)
-
在調(diào)用函數(shù)時(shí),按照從左到右的順序依次定義實(shí)參,稱為位置實(shí)參,凡是按照這種形式定義的實(shí)參會按照從左到右的順序與形參一一對應(yīng)
>>> register('lili',18,'male') #對應(yīng)關(guān)系為:name=’lili’,age=18,sex=’male’ Name:lili Age:18 Sex:male
2.2 關(guān)鍵字參數(shù)
在調(diào)用函數(shù)時(shí),實(shí)參可以是key=value的形式,稱為關(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ù)后面,且不可以對一個形參重復(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)為形參賦值,這類形參稱之為默認(rèn)參數(shù),當(dāng)函數(shù)有多個參數(shù)時(shí),需要將值經(jīng)常改變的參數(shù)定義成位置參數(shù),而將值改變較少的參數(shù)定義成默認(rèn)參數(shù)。例如編寫一個注冊學(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í)可以不對sex賦值,這降低了函數(shù)調(diào)用的復(fù)雜度
>>> register('tom',17) #大多數(shù)情況,無需為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無任何關(guān)系
>>> foo()
1
-
默認(rèn)參數(shù)的值通常應(yīng)設(shè)為不可變類型
>>> 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 可變長度的參數(shù)(與*的用法)
參數(shù)的長度可變指的是在調(diào)用函數(shù)時(shí),實(shí)參的個數(shù)可以不固定,而在調(diào)用函數(shù)時(shí),實(shí)參的定義無非是按位置或者按關(guān)鍵字兩種形式,這就要求形參提供兩種解決方案來分別處理兩種形式的可變長度的參數(shù)
2.4.1 可變長度的位置參數(shù)
如果在最后一個形參名前加號,那么在調(diào)用函數(shù)時(shí),溢出的位置實(shí)參,都會被接收,以元組的形式保存下來賦值給該形參
>>> def foo(x,y,z=1,*args): #在最后一個形參名args前加*號
... 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都被*接收,以元組的形式保存下來,賦值給args,即args=(4, 5, 6,7)
1
2
3
(4, 5, 6, 7)
如果我們事先生成了一個列表,仍然是可以傳值給*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í)沒有加*,那L就只是一個普通的位置參數(shù)了
>>> foo(1,2,L) #僅多出一個位置實(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
如果我們想要求多個值的和,*args就派上用場了
>>> def add(*args):
... res=0
... for i in args:
... res+=i
... return res
...
>>> add(1,2,3,4,5)
15
2.4.2 可變長度的關(guān)鍵字參數(shù)
如果在最后一個形參名前加號,那么在調(diào)用函數(shù)時(shí),溢出的關(guān)鍵字參數(shù),都會被接收,以字典的形式保存下來賦值給該形參
>>> def foo(x,**kwargs): #在最后一個參數(shù)kwargs前加**
... print(x)
... print(kwargs)
...
>>> foo(y=2,x=1,z=3) #溢出的關(guān)鍵字實(shí)參y=2,z=3都被**接收,以字典的形式保存下來,賦值給kwargs
1
{'z': 3, 'y': 2}
如果我們事先生成了一個字典,仍然是可以傳值給**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í)沒有加**,那dic就只是一個普通的位置參數(shù)了
>>> foo(1,2,dic) #TypeError:函數(shù)foo只需要2個位置參數(shù),但是傳了3個
如果形參為常規(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
如果我們要編寫一個用戶認(rèn)證的函數(shù),起初可能只基于用戶名密碼的驗(yàn)證就可以了,可以使用**kwargs為日后的擴(kuò)展供良好的環(huán)境,同時(shí)保持了函數(shù)的簡潔性。
>>> def auth(user,password,**kwargs):
... pass
...
2.5 命名關(guān)鍵字參數(shù)
在定義了**kwargs參數(shù)后,函數(shù)調(diào)用者就可以傳入任意的關(guān)鍵字參數(shù)key=value,如果函數(shù)體代碼的執(zhí)行需要依賴某個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提供了專門的語法:需要在定義形參時(shí),用作為一個分隔符號,號之后的形參稱為命名關(guān)鍵字參數(shù)。對于這類參數(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沒有為命名關(guān)鍵字參數(shù)height傳值。
命名關(guān)鍵字參數(shù)也可以有默認(rè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)槎呔?em>后,所以都是命名關(guān)鍵字參數(shù),形參sex=’male’屬于命名關(guān)鍵字參數(shù)的默認(rèn)值,因而即便是放到形參height之前也不會有問題。另外,如果形參中已經(jīng)有一個args了,命名關(guān)鍵字參數(shù)就不再需要一個單獨(dú)的*作為分隔符號了
>>> 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通常是組合在一起使用的,如果一個函數(shù)的形參為args與**kwargs,那么代表該函數(shù)可以接收任何形式、任意長度的參數(shù)
>>> def wrapper(*args,**kwargs):
... pass
...
在該函數(shù)內(nèi)部還可以把接收到的參數(shù)傳給另外一個函數(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
按照上述寫法,在為函數(shù)wrapper傳參時(shí),其實(shí)遵循的是函數(shù)func的參數(shù)規(guī)則,調(diào)用函數(shù)wrapper的過程分析如下:
- 位置實(shí)參1被接收,以元組的形式保存下來,賦值給args,即args=(1,),關(guān)鍵字實(shí)參z=3,y=2被*接收,以字典的形式保存下來,賦值給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被替換成其他名字并無語法錯誤,但使用args、kwargs是約定俗成的。