函數定義支持可變數量的參數。下面列出三種可以組合使用的形式。
1 默認值參數
為參數指定默認值,在調用函數時可以使用比定義時更少的參數。
def student_info(name, sex, age=18, score=100):
if sex == 'male':
print(f"{name} is {age} years old, his score is {score}")
else:
print(f"{name} is {age} years old, her score is {score}")
該函數接收兩個必選參數name
和 sex
,兩個可選參數 age
,score
。該函數可用下列方式調用
- 只傳入必選參數
student_info('ZhangSan', 'male')
# zhangSan is 18 years old, his score is 100.
- 傳入一個可選參數
student_info('LiSi', 'female', 17)
# LiSi is 17 years old, her score is 100.
- 傳入所有實參
student_info('XiaoHong', 'female', 17, 99)
# XiaoHong is 17 years old, her score is 99.
2 關鍵字參數
kwarg=value
形式的 關鍵字參數 也可以用於調用函數。
def student_info(name, sex, age=18, score=100):
if sex == 'male':
print(f"{name} is {age} years old, his score is {score}")
else:
print(f"{name} is {age} years old, her score is {score}")
使用了默認值參數的函數都可以通過kwarg=value
形式的關鍵字參數調用函數。該函數可以通過下列方式調用。
student_info('ZhangSan', 'male')
student_info('ZhangSan', 'male', age=17)
student_info('ZhangSan', 'male', score=99)
student_info('ZhangSan', 'male', age=19, score=98)
student_info('ZhangSan', 'male', score=95, age=20)
從上面調用函數的方式可以看出:
- 關鍵字參數的順序可以和定義的順序不一致
- 關鍵字參數必須放在位置參數之后
如果你想打亂所有參數的順序,包括位置參數,那么你需要對所有參數使用kwarg=value
的方式來傳入實參。如:
student_info(score=100, age=19, name='ZhangSan', sex='malae')
不能在一次調用中多次對同一個參數進行賦值(即使多次賦值都是同一個值):
student_info('ZhangSan', 'male', name='ZhangSan')
這樣是不被允許的。
**name
當最后一個形參為**name
形式時,接收一個字典,該字典包含與函數中已定義形參之外的所有關鍵字參數。**name
可以和*name
形參組合使用(注意:*name
必須在**name
前面),*name
接收一個元組,該元組包含形參列表之外的位置參數。
其中**name
接收的字典中數據的順序和調用函數時實參傳入的順序一致。
3 特殊參數
默認情況下,參數可以按位置或顯式關鍵字傳遞給 Python 函數。為了讓代碼易讀、高效,最好限制參數的傳遞方式,這樣,開發者只需查看函數定義,即可確定參數項是僅按位置、按位置或關鍵字,還是僅按關鍵字傳遞。需要使用/
和*
。
函數定義如下:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ----------- ----------
| | |
| | |
位置參數 位置參數或關鍵字參數 關鍵字參數
其中/
和*
是可選的。
3.1 位置或關鍵字參數
當函數定義中未使用/
和*
時,參數可以按照位置或關鍵字傳遞給函數。
3.2 僅位置參數
特定形參可以標記為 僅限位置。僅限位置 時,形參的順序很重要,且這些形參不能用關鍵字傳遞。僅限位置形參應放在 /
(正斜杠)前。/
用於在邏輯上分割僅限位置形參與其它形參。如果函數定義中沒有 /
,則表示沒有僅限位置形參。
/
后可以是 位置或關鍵字 或 僅限關鍵字 形參。
3.3 僅限關鍵字參數
把形參標記為僅限關鍵字,表明必須以關鍵字參數的形式傳遞該參數。
3.4 函數示例
def standard_arg(arg):
print(arg)
def pos_only_arg(arg, /):
print(arg)
def kwd_only_arg(*, arg):
print(arg)
def combined_example(pos_only, /, standard, *, kwd_only):
print(pos_only, standard, kwd_only)
第一個函數 standard_arg
的函數定義對調用的方式沒有任何限制,可以按位置也可以按關鍵字傳遞參數:
standard_aeg(2)
standard_aeg(arg=2)
第二個函數 pos_only_arg
的函數定義中有 /
,且形參全在 /
之前定義,所以僅限使用位置參數。
pos_only_arg(3)
pos_only_arg(arg=3) # 無效的調用
# TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'
第三個函數 kwd_only_arg
的函數定義通過 *
表明僅限關鍵字參數。
kwd_only_arg(arg=5)
kwd_only_arg(5) # 無效的調用
# TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given
最后一個函數 combined_example
的函數定義中,使用了全部三種調用慣例:
- 該函數接收三個參數
- 第一個參數僅限位置參數
- 第二個參數可使用位置或關鍵字參數
- 第三個參數僅限關鍵字參數
combined_example(6, 7, kwd_only=8)
combined_example(6, standard=7, kwd_only=8)
下面的函數定義中,kwds
里面不能把 name
當做鍵。會與第一個參數 name
沖突。
def foo(name, **kwds):
print(name, **kwds)
foo(1, **{name: 2})
因為上面的函數調用相當於把name
賦值了兩次。等價於:
foo(1, name=2)
由於此時name
既可以當作位置參數,也可以當作關鍵字參數,所以上述行為相當於給一個參數在一次函數調用中賦值了兩次。
那么如何才能讓 kwds
使用 name
當作鍵呢?
這就需要使用 特殊參數 /
來解決了,只要把 name
參數僅限使用位置參數,那么再使用name=2
時就不會被認為是第二次賦值了。
所以需要把函數定義為:
def foo(name, /, **kwds):
print(name, kwds)
foo(1, name=2, age=18)
# 1 {'name': 2, 'age': 18}
就可以了。👼
總結:僅限位置形參的名稱可以在**kwds
中使用,而不產生歧義。
4 任意實參列表
調用函數時,使用任意數量的實參是最少見的選項。這些實參包含在元組中。
有時候,需要接受任意數量的實參,但預先不知道傳遞給函數的會是什么樣的信息。在這種 情況下,可將函數編寫成能夠接受任意數量的鍵值對——調用語句提供了多少就接受多少。
def sumnum(a, *ddd):
print(a)
result = a
for num in ddd:
result += num
return result
print(sumnum(1, 2, 3, 5, 6, 9, 8, 7))
# 1
# 41
*name
用於接收傳遞給函數的所有剩余位置參數,因此*name
的后面不允許出現位置參數,而是僅限關鍵字參數,同樣的*name
的前面也不允許出現任何的關鍵字參數,而是僅限位置參數。
5 解包實參列表
函數調用要求獨立的位置參數,但實參在列表或元組里時,要執行相反的操作。例如,內置的 range() 函數要求獨立的 start 和 stop 實參。如果這些參數不是獨立的,則要在調用函數時,用 *
操作符把實參從列表或元組解包出來:
>>> list(range(3, 6))
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args))
[3, 4, 5]
同樣,字典可以用 **
操作符傳遞關鍵字參數。
6 Lambda 表達式
Lambda 關鍵字用於創建小巧的匿名函數。
Lambda a, b: a+b
上面的函數返回兩個參數的和。Lambda 函數可用於任何需要函數對象的地方。在語法上,匿名函數只能是單個表達式。在語義上,它只是常規函數定義的語法糖。與嵌套函數定義一樣,lambda 函數可以引用包含作用域中的變量:
def make_incrementor(n):
return lambda x: x + n
f = make_incrementor(42)
print(f(0))
# 42
print(f(1))
# 43
上例用 lambda 表達式返回函數。還可以把匿名函數用作傳遞的實參:
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key=lambda pa: pa[1])
print(pairs)
# [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]