Python: 函數(shù)參數(shù)魔法
2016-12-01
函數(shù)參數(shù)
在 Python 中,定義函數(shù)和調(diào)用函數(shù)都很簡單局义,但如何定義函數(shù)參數(shù)和傳遞函數(shù)參數(shù),則涉及到一些套路了檩帐。總的來說诅挑,Python 的函數(shù)參數(shù)主要分為以下幾種:
必選參數(shù)
默認(rèn)參數(shù)
可變參數(shù)
關(guān)鍵字參數(shù)
必選參數(shù)
必選參數(shù)可以說是最常見的了拔妥,顧名思義没龙,必選參數(shù)就是在調(diào)用函數(shù)的時候要傳入數(shù)量一致的參數(shù)兜畸,比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> def add(x, y): ? ? ? ?# x, y 是必選參數(shù)
... ? ? print x + y
...
>>> add() ? ? ? ? ? ? ? ? # 啥都沒傳咬摇,不行
Traceback (most recent call last):
?File "", line 1, in
TypeError: add() takes exactly 2 arguments (0 given)
>>> add(1) ? ? ? ? ? ? ? ?# 只傳了一個肛鹏,也不行
Traceback (most recent call last):
File "", line 1, in
TypeError: add() takes exactly 2 arguments (1 given)
>>> add(1, 2) ? ? ? ? ? ? # 數(shù)量一致恩沛,通過
3
默認(rèn)參數(shù)
默認(rèn)參數(shù)是指在定義函數(shù)的時候提供一些默認(rèn)值芒珠,如果在調(diào)用函數(shù)的時候沒有傳遞該參數(shù)皱卓,則自動使用默認(rèn)值部逮,否則使用傳遞時該參數(shù)的值。
看看例子就明白了:
1
2
3
4
5
6
7
>>> def add(x, y, z=1): ? ? # x, y 是必選參數(shù)掐禁,z 是默認(rèn)參數(shù)缕允,默認(rèn)值是 1
... ? ? print x + y + z
...
>>> add(1, 2, 3) ? ? ? ? ? ?# 1+2+3
6
>>> add(1, 2) ? ? ? ? ? ? ? # 沒有傳遞 z障本,自動使用 z=1般又,即 1+2+1
4
可以看到茴迁,默認(rèn)參數(shù)使用起來也很簡單堕义,但有兩點(diǎn)需要注意的是:
默認(rèn)參數(shù)要放在所有必選參數(shù)的后面
默認(rèn)參數(shù)應(yīng)該使用不可變對象
比如倦卖,下面對默認(rèn)參數(shù)的使用是錯誤的:
1
2
3
4
5
6
7
8
9
10
11
>>> def add(x=1, y, z): ? ? ?# x 是默認(rèn)參數(shù)怕膛,必須放在所有必選參數(shù)的后面
... ? ? return x + y + z
...
?File "", line 1
SyntaxError: non-default argument follows default argument
>>>
>>> def add(x, y=1, z): ? ? ?# y 是默認(rèn)參數(shù)褐捻,必須放在所有必選參數(shù)的后面
... ? ? return x + y + z
...
?File "", line 1
SyntaxError: non-default argument follows default argument
再來看看為什么默認(rèn)參數(shù)應(yīng)該使用不可變對象。
我們看一個例子:
1
2
3
>>> def add_to_list(L=[]):
... ? ? L.append('END')
... ? ? return L
在上面的函數(shù)中景馁,L 是一個默認(rèn)參數(shù)绰精,默認(rèn)值是 []透葛,表示空列表阱表。
我們來看看使用:
1
2
3
4
5
6
7
8
9
10
>>> add_to_list([1, 2, 3]) ? ? ? ? ?# 沒啥問題
[1, 2, 3, 'END']
>>> add_to_list(['a', 'b', 'c']) ? ?# 沒啥問題
['a', 'b', 'c', 'END']
>>> add_to_list() ? ? ? ? ? ? ? ? ? # 沒有傳遞參數(shù)涉馁,使用默認(rèn)值烤送,也沒啥問題
['END']
>>> add_to_list() ? ? ? ? ? ? ? ? ? # 沒有傳遞參數(shù)帮坚,使用默認(rèn)值试和,竟出現(xiàn)兩個 'END'
['END', 'END']
>>> add_to_list() ? ? ? ? ? ? ? ? ? # 糟糕了,三個 'END'
['END', 'END', 'END']
為啥呢纫普?我們在調(diào)用函數(shù)的時候沒有傳遞參數(shù)阅悍,那么就默認(rèn)使用 L=[],經(jīng)過處理昨稼,L 應(yīng)該只有一個元素节视,怎么會出現(xiàn)調(diào)用函數(shù)兩次,L 就有兩個元素呢假栓?
原來寻行,L 指向了可變對象 [],當(dāng)你調(diào)用函數(shù)時匾荆,L 的內(nèi)容發(fā)生了改變拌蜘,默認(rèn)參數(shù)的內(nèi)容也會跟著變,也就是,當(dāng)你第一次調(diào)用時,L 的初始值是 [],當(dāng)你第二次調(diào)用時链蕊,L 的初始值是 ['END']陪蜻,等等症昏。
所以础米,為了避免不必要的錯誤,我們應(yīng)該使用不可變對象作為函數(shù)的默認(rèn)參數(shù)竖瘾。
可變參數(shù)
在某些情況下庸论,我們在定義函數(shù)的時候,無法預(yù)估函數(shù)應(yīng)該制定多少個參數(shù)趋观,這時我們就可以使用可變參數(shù)了麸恍,也就是瓤球,函數(shù)的參數(shù)個數(shù)是不確定的欠肾。
看看例子:
1
2
3
4
5
6
>>> def add(*numbers):
... ? ? sum = 0
... ? ? for i in numbers:
... ? ? ? ? sum += i
... ? ? print 'numbers:', numbers
... ? ? return sum
在上面的代碼中瑟慈,numbers 就是一個可變參數(shù),參數(shù)前面有一個 * 號,表示是可變的。在函數(shù)內(nèi)部堆生,參數(shù) numbers 接收到的是一個 tuple。
在調(diào)用函數(shù)時桥温,我們可以給該函數(shù)傳遞任意個參數(shù)氛谜,包括 0 個參數(shù):
1
2
3
4
5
6
7
8
9
10
11
12
>>> add() ? ? ? ? ? # 傳遞 0 個參數(shù)
numbers: ()
0
>>> add(1) ? ? ? ? ?# 傳遞 1 個參數(shù)
numbers: (1,)
1
>>> add(1, 2) ? ? ? # 傳遞 2 個參數(shù)
numbers: (1, 2)
3
>>> add(1, 2, 3) ? ?# 傳遞 3 個參數(shù)
numbers: (1, 2, 3)
6
上面的 * 表示任意參數(shù)悔政,實(shí)際上芦瘾,它還有另外一個用法:用來給函數(shù)傳遞參數(shù)。
看看例子:
1
2
3
4
5
6
7
8
9
10
11
>>> def add(x, y, z): ? ? ? ?# 有 3 個必選參數(shù)
... return x + y + z
...
>>> a = [1, 2, 3]
>>> add(a[0], a[1], a[2]) ? ?# 這樣傳遞參數(shù)很累贅
6
>>> add(*a) ? ? ? ? ? ? ? ? ?# 使用 *a,相當(dāng)于上面的做法
6
>>> b = (4, 5, 6)
>>> add(*b) ? ? ? ? ? ? ? ? ?# 對元組一樣適用
15
再看一個例子:
1
2
3
4
5
6
7
8
9
10
11
12
>>> def add(*numbers): ? ? ? # 函數(shù)參數(shù)是可變參數(shù)
... sum = 0
... for i in numbers:
... ? ?sum += i
... return sum
...
>>> a = [1, 2]
>>> add(*a) ? ? ? ? ? ? ? ? ?# 使用 *a 給函數(shù)傳遞參數(shù)
3
>>> a = [1, 2, 3, 4]
>>> add(*a)
10
關(guān)鍵字參數(shù)
可變參數(shù)允許你將不定數(shù)量的參數(shù)傳遞給函數(shù)哄陶,而關(guān)鍵字參數(shù)則允許你將不定長度的鍵值對, 作為參數(shù)傳遞給一個函數(shù)。
讓我們看看例子:
1
2
3
4
5
6
7
8
>>> def add(**kwargs):
? ?return kwargs
>>> add() ? ? ? ? ? ?# 沒有參數(shù),kwargs 為空字典
{}
>>> add(x=1) ? ? ? ? # x=1 => kwargs={'x': 1}
{'x': 1}
>>> add(x=1, y=2) ? ?# x=1, y=2 => kwargs={'y': 2, 'x': 1}
{'y': 2, 'x': 1}
在上面的代碼中齿诉,kwargs 就是一個關(guān)鍵字參數(shù)歇竟,它前面有兩個 * 號馋记。kwargs 可以接收不定長度的鍵值對壁肋,在函數(shù)內(nèi)部,它會表示成一個 dict。
和可變參數(shù)類似阁吝,我們也可以使用 **kwargs 的形式來調(diào)用函數(shù),比如:
1
2
3
4
5
6
7
8
>>> def add(x, y, z):
... return x + y + z
...
>>> dict1 = {'z': 3, 'x': 1, 'y': 6}
>>> add(dict1['x'], dict1['y'], dict1['z']) ? ?# 這樣傳參很累贅
10
>>> add(**dict1) ? ? ? ?# 使用 **dict1 來傳參械拍,等價于上面的做法
10
再看一個例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> def sum(**kwargs): ? ? ? ? ? ? ? # 函數(shù)參數(shù)是關(guān)鍵字參數(shù)
... sum = 0
... for k, v in kwargs.items():
... ? ?sum += v
... return sum
>>> sum() ? ? ? ? ? ? ? ? ? ? ? ? ? ?# 沒有參數(shù)
0
>>> dict1 = {'x': 1}
>>> sum(**dict1) ? ? ? ? ? ? ? ? ? ? # 相當(dāng)于 sum(x=1)
1
>>> dict2 = {'x': 2, 'y': 6}
>>> sum(**dict2) ? ? ? ? ? ? ? ? ? ? # 相當(dāng)于 sum(x=2, y=6)
8
參數(shù)組合
在實(shí)際的使用中,我們經(jīng)常會同時用到必選參數(shù)装盯、默認(rèn)參數(shù)坷虑、可變參數(shù)和關(guān)鍵字參數(shù)或其中的某些。但是埂奈,需要注意的是迄损,它們在使用的時候是有順序的,依次是必選參數(shù)账磺、默認(rèn)參數(shù)芹敌、可變參數(shù)和關(guān)鍵字參數(shù)痊远。
比如,定義一個包含上述四種參數(shù)的函數(shù):
1
2
3
4
5
6
>>> def func(x, y, z=0, *args, **kwargs):
? ?print 'x =', x
? ?print 'y =', y
? ?print 'z =', z
? ?print 'args =', args
? ?print 'kwargs =', kwargs
在調(diào)用函數(shù)的時候氏捞,Python 會自動按照參數(shù)位置和參數(shù)名把對應(yīng)的參數(shù)傳進(jìn)去碧聪。讓我們看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>>> func(1, 2) ? ? ? ? ? ? ? ? ? ? # 至少提供兩個參數(shù),因為 x, y 是必選參數(shù)
x = 1
y = 2
z = 0
args = ()
kwargs = {}
>>> func(1, 2, 3) ? ? ? ? ? ? ? ? ?# x=1, y=2, z=3
x = 1
y = 2
z = 3
args = ()
kwargs = {}
>>> func(1, 2, 3, 4, 5, 6) ? ? ? ? # x=1, y=2, z=3, args=(4, 5, 6), kwargs={}
x = 1
y = 2
z = 3
args = (4, 5, 6)
kwargs = {}
>>> func(1, 2, 4, u=6, v=7) ? ? ? ?# args = (), kwargs = {'u': 6, 'v': 7}
x = 1
y = 2
z = 4
args = ()
kwargs = {'u': 6, 'v': 7}
>>> func(1, 2, 3, 4, 5, u=6, v=7) ? # args = (4, 5), kwargs = {'u': 6, 'v': 7}
x = 1
y = 2
z = 3
args = (4, 5)
kwargs = {'u': 6, 'v': 7}
我們還可以通過下面的形式來傳遞參數(shù):
1
2
3
4
5
6
7
8
>>> a = (1, 2, 3)
>>> b = {'u': 6, 'v': 7}
>>> func(*a, **b)
x = 1
y = 2
z = 3
args = ()
kwargs = {'u': 6, 'v': 7}
小結(jié)
默認(rèn)參數(shù)要放在所有必選參數(shù)的后面液茎。
應(yīng)該使用不可變對象作為函數(shù)的默認(rèn)參數(shù)逞姿。
*args 表示可變參數(shù),**kwargs 表示關(guān)鍵字參數(shù)捆等。
參數(shù)組合在使用的時候是有順序的滞造,依次是必選參數(shù)、默認(rèn)參數(shù)栋烤、可變參數(shù)和關(guān)鍵字參數(shù)。
*args 和 **kwargs 是 Python 的慣用寫法明郭。
參考資料
args 和 *kwargs · Python進(jìn)階
函數(shù)的參數(shù) - 廖雪峰的官方網(wǎng)站