從零開始學Python-Day17-函數參數(2)

上一節說了參數位置和默認參數,這一節著重說可變參數和關鍵字參數


從零開始學Python-Day17-函數參數(2)


可變參數

可變參數就是傳入的參數個數是可變的,可以任意個參數,包括0個參數。我們以計算a2 + b2 + c2 + ……為例要定義這個函數,我們可以把需要傳入的參數以一個list或者tuple傳入,定義如下,這裡我們用到了for循環,從list或tuple中循環取值,執行命令:

<code>>>> def f(num):
\tsum=0
\tfor n in num:
\t\tsum=sum+n*n
\treturn sum

>>> f((1,2,3))
14
>>> f([2,3,4])
29
>>> f(1,2,3)
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
f(1,2,3)
TypeError: f() takes 1 positional argument but 3 were given/<module>/<pyshell>/<code>

我們看到傳入的參數為list和tuple時可以很好的完成任務,但是我們想簡單點直接寫一組數字就報錯了。這個時候就需要用到可變參數。在定義時,num前加*號,這樣函數會把傳入的參數按一個tuple接收:

<code>>>> def f(*num):
\tsum=0
\tfor n in num:
\t\tsum=sum+n*n
\treturn sum


>>> f(1,2,3)
14
>>> f(2,3,4)
29
>>> /<code>

那麼如果說我們已經有一個list或者tuple,想對整個list或tiple調用函數f該如何調用呢,一種方法是直接用list的定位來完成參數傳入:

<code>>>> l=[3,4,5]
>>> f(l[0],l[1],l[2])
50
>>> s=(3,4,5)
>>> f(s[0],s[1],s[2])
50/<code>

這樣操作顯然有些麻煩,如果list裡內容太多也一樣寫不過來,這時可以用第二種方法,可以直接在函數調用時,在list或者tuple前面加*進行傳參,如下:

<code>>>> f(*l)
50
>>> f(*s)
50/<code>

關鍵字參數

關鍵詞參數允許函數傳入0個或多個含有參數名的參數,在函數內部組裝為一個字典dict

<code>>>> def person(name,age,**other):
\tprint('name:',name,'age:',age,'other',other)
>>> person('張三',35)
name: 張三 age: 35 other {}
>>> person('李四',36,city='北京')
name: 李四 age: 36 other {'city': '北京'}
>>> person('王五',40,city='北京',site='www.woodmangzhang.com')

name: 王五 age: 40 other {'city': '北京', 'site': 'www.woodmangzhang.com'}/<code>

上面這個示例有點像我們在用戶註冊時,有些是必填信息,有些是非必填項,但是如果用戶願意提交就都可以提交。關鍵詞參數也可以調用已經組裝好的dict來完成傳參,如下:

<code>>>> others={'city':'北京','site':'www.woodmangzhang.com'}
>>> person('趙六',29,**others)
name: 趙六 age: 29 other {'city': '北京', 'site': 'www.woodmangzhang.com'}/<code>

可變參數是一個*,關鍵詞參數是兩個*,在定義和調用時都是一樣的用法,只不過一個是list或tuple,一個是dict。上面的例子中注意others是一個外部的dict,函數內部的other只是從外部的others取值,內部other的改變並不影響外部的others。

命名關鍵詞參數

那用了關鍵詞參數,使用者不是隨便傳參數內容進來,這就需要函數內部做檢查,仍然以上面的問題為例,我們要檢查是否有city和site:

<code>>>> def person(name,age,**other):
\tif 'city' in other:
\t\tpass
\tif 'site' in other:
\t\tpass
\tprint(name,age,other)

>>> person('張三',15,city='北京',site='www.woodmanzhang.com')
張三 15 {'city': '北京', 'site': 'www.woodmanzhang.com'}
>>> person('張三',15,city='北京',site='www.woodmanzhang.com',homeadd='中關村壹號')
張三 15 {'city': '北京', 'site': 'www.woodmanzhang.com', 'homeadd': '中關村壹號'}/<code>

可以看到調用者依然可以隨意傳入數據,如果我們要限制只接受city和site作為關鍵詞參數,可以用*,關鍵詞1,關鍵詞2表示,也就是命名關鍵詞:

<code>>>> def person(name,age,*,city,site):
\tprint(name,age,city,site)

>>> person('張三',21,city='北京',site='www.woodmanzhang.com')
張三 21 北京 www.woodmanzhang.com
>>> person('張三',21,city='北京',site='www.woodmanzhang.com',homeadd='中關村壹號')
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
person('張三',21,city='北京',site='www.woodmanzhang.com',homeadd='中關村壹號')
TypeError: person() got an unexpected keyword argument 'homeadd'
>>> person('木人張',35)
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
person('木人張',35)
TypeError: person() missing 2 required keyword-only arguments: 'city' and 'site'
>>> person('木人張',35,'北京','www.woodmangzhang.com')
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
person('木人張',35,'北京','www.woodmangzhang.com')
TypeError: person() takes 2 positional arguments but 4 were given/<module>/<pyshell>/<module>/<pyshell>/<module>/<pyshell>/<code>

可見,當設定了命名關鍵詞參數,對應的參數就成了必選參數,當我們多輸入和少輸入命名關鍵詞參數時都會報錯;對於命名關鍵詞參數,不按關鍵詞傳入也一樣會報錯!因為沒有加關鍵詞傳入,函數理解為傳入了四個位置參數,但定義函數時指指定了2個位置參數。而如果函數中已經有了一個可變參數,那麼後面跟著的命名關鍵詞參數就都不再需要*來分隔:

<code>>>> def person(name,age,*s,city,site):
\tprint(name,age,s,city,site)/<code>

命名關鍵詞還可以設定缺省默認值,從而簡化調用時的傳入,如下有了默認值,我們傳參時可以不傳入這一項:

<code>>>> def person(name,age,*,city='北京',site):
\tprint(name,age,city,site)

>>> person('張三',18,site='woodmanzhang.com')
張三 18 北京 woodmanzhang.com/<code>

需要注意的是,*的區隔必須有,可以是一個可變參數,或者沒有可變參數用*, 如果沒有*這一標識,程序會認為需要傳入的都是位置參數,無法識別關鍵詞參數。另外,關鍵詞參數是可以調換順序的,這也是它跟位置參數的一個區別,如下:

<code>>>> person('張三',18,site='woodmanzhang.com',city='上海')
張三 18 上海 woodmanzhang.com/<code>

參數組合

必須參數、默認參數、可變參數、命名關鍵字參數、關鍵字參數可以組合使用,即為參數組合,注意參數定義的順序,也要按這一順序。

<code>>>> def f1(a,b,c=0,*args,**kw):
\tprint(a,b,c,args,kw)

>>> def f2(a,b,c=0,*,d,**kw):
\tprint(a,b,c,d,kw)

>>> f1(1,2,3)
1 2 3 () {}
>>> f2(1,2,3)
>>> f2(1,2,3,d=4,n=5)
1 2 3 4 {'n': 5}
>>> f1(1,2,3,('a','b'),'nono')
1 2 3 (('a', 'b'), 'nono') {}
>>> args = (1, 2, 3, 4)
>>> kw = {'d': 99, 'x': '#'}
>>> args=(1,2,3,4)
>>> kw={'d':99,'e':100}
>>> f1(*args,**kw)
1 2 3 (4,) {'d': 99, 'e': 100}
>>> args=(1,2,3)
>>> f2(*args,**kw)
1 2 3 99 {'e': 100}
>>> args=[1,2,3]
>>> f2(*args,**kw)
1 2 3 99 {'e': 100}/<code>

函數調用時,不論是手動傳入的參數,還是list或者tuple和dict,都可以給函數傳參調用。


分享到:


相關文章: