測(cè)試只能證明程序有錯(cuò)誤个唧,而不能證明程序沒(méi)有錯(cuò)誤江解。
—— Edsger Dijkstra
目錄
本節(jié)我們來(lái)介紹函數(shù)
,在前邊的章節(jié)我們已經(jīng)接觸了挺多函數(shù)了徙歼,比如print()
犁河,len()
等鳖枕。
函數(shù)是編程語(yǔ)言中非常重要的概念,函數(shù)是一段可重復(fù)利用
的桨螺,提供特定功能
的代碼段宾符。
函數(shù)是一種模塊化
的手段,可提高代碼的利用率灭翔,避免重復(fù)代碼魏烫,便于使用,便于維護(hù)肝箱。
Python 中哄褒,不僅提供了許多現(xiàn)成可用的內(nèi)建函數(shù)
,用戶(hù)還可以根據(jù)自己的需求煌张,定義自己的函數(shù)
呐赡。
在Python 中,內(nèi)建函數(shù)用builtin_function_or_method
表示唱矛,自定義函數(shù)用function
表示罚舱。
函數(shù)也屬于一種數(shù)據(jù)類(lèi)型,可用type()
查看:
>>> type(len) # len() 是內(nèi)建函數(shù)
<class 'builtin_function_or_method'>
>>>
>>> type(print) # print() 是內(nèi)建函數(shù)
<class 'builtin_function_or_method'>
>>>
>>> def test(): pass # 自定義函數(shù) test()绎谦,并沒(méi)有實(shí)質(zhì)功能
...
>>>
>>> type(test) # test() 是自定義函數(shù)
<class 'function'>
提示:
內(nèi)建函數(shù)
就是Python 原生支持管闷,自帶的函數(shù)(比如print()
),用于區(qū)分用戶(hù)自定函數(shù)
窃肠。
1包个,函數(shù)的定義
函數(shù)定義的格式如下:
def fun_name(參數(shù)列表...):
# 函數(shù)體
...
首先,定義函數(shù)需要用到def
關(guān)鍵字冤留,后跟函數(shù)名
碧囊,然后是參數(shù)列表
,參數(shù)列表寫(xiě)在小括號(hào)()
內(nèi)纤怒,再往后是一個(gè)冒號(hào):
糯而,函數(shù)體
一般寫(xiě)在下一行,注意要有縮進(jìn)
泊窘。
例如熄驼,我們定義一個(gè)簡(jiǎn)單的函數(shù):
def hello_python():
print('hello python.')
該函數(shù)值輸出一個(gè)字符串hello python.
,其中:
-
hello_python
為函數(shù)名 - 參數(shù)列表為空烘豹,調(diào)用該函數(shù)時(shí)瓜贾,不需要任何參數(shù)
- 函數(shù)體只有一句代碼
print('hello python.')
文檔字符串
我們可以在函數(shù)體的第一行,寫(xiě)一段該函數(shù)的說(shuō)明携悯,此說(shuō)明用三引號(hào)'''
或 """
引住祭芦,這被稱(chēng)為文檔字符串
。如下:
def hello_python():
'''這是函數(shù)的一段說(shuō)明'''
print('hello python.')
要想查看這段說(shuō)明憔鬼,可以使用格式函數(shù)名.__doc__
來(lái)訪(fǎng)問(wèn)龟劲,如下:
>>> hello_python.__doc__
'這是函數(shù)的一段說(shuō)明'
我們也可以訪(fǎng)問(wèn)內(nèi)建函數(shù)的文檔字符串胃夏,比如len()
函數(shù):
>>> len.__doc__
'Return the number of items in a container.'
2,調(diào)用函數(shù)
調(diào)用函數(shù)時(shí)咸灿,只需要用函數(shù)名
和參數(shù)列表
就行构订,比如上面的函數(shù),調(diào)用如下:
hello_python() # 輸出 hello python.
其實(shí)避矢,在Python 中悼瘾,函數(shù)
也是一種類(lèi)型,函數(shù)名
就是一個(gè)函數(shù)類(lèi)型
的變量审胸。
因此亥宿,可以將一個(gè)函數(shù)名賦值給一個(gè)變量,如下:
abc = hello_python
print(type(abc)) # 輸出 <class 'function'>
abc() # 就像調(diào)用 hello_python() 一樣
3砂沛,函數(shù)參數(shù)
定義函數(shù)時(shí)烫扼,可以傳給函數(shù)一些數(shù)據(jù),這些數(shù)據(jù)叫做函數(shù)參數(shù)
碍庵。
定義一個(gè)帶參數(shù)的函數(shù)映企,如下:
def hello(string):
print('hello %s.' % string)
其中的string
就是函數(shù)參數(shù),函數(shù)參數(shù)可以有多個(gè)静浴,這里我們只寫(xiě)了一個(gè)參數(shù)堰氓。
此時(shí),調(diào)用該函數(shù)時(shí)苹享,就需要給函數(shù)傳遞參數(shù)了双絮,如下:
hello('python') # 輸出 hello python.
hello('java') # 輸出 hello java.
hello('html') # 輸出 hello html.
如果參數(shù)傳遞出現(xiàn)錯(cuò)誤,則會(huì)出現(xiàn)TypeError
異常得问,參數(shù)傳遞錯(cuò)誤包括:
- 參數(shù)個(gè)數(shù)不對(duì)
- 參數(shù)類(lèi)型不適用
4囤攀,默認(rèn)參數(shù)
定義參數(shù)時(shí),可以包含默認(rèn)參數(shù)
宫纬,默認(rèn)參數(shù)可以有多個(gè)焚挠。
默認(rèn)參數(shù)就是,這個(gè)參數(shù)可以傳遞漓骚,也可以不傳遞:
- 傳遞時(shí)宣蔚,該參數(shù)就是你傳遞的值
- 不傳遞時(shí),該參數(shù)有默認(rèn)值
如下认境,我們定義了一個(gè)帶有默認(rèn)參數(shù)的函數(shù):
def sum(a, b=0, c=1):
n = a + b + c
print(n)
默認(rèn)參數(shù)必須寫(xiě)在普通參數(shù)的后邊,默認(rèn)參數(shù)后邊不能再有普通參數(shù)挟鸠。
其中叉信,參數(shù)a
是普通參數(shù),參數(shù)b
和c
都是默認(rèn)參數(shù)艘希,用賦值語(yǔ)句
賦予了它們默認(rèn)值硼身。
參數(shù)b
的默認(rèn)值是0
硅急,參數(shù)c
的默認(rèn)值是1
。
當(dāng)沒(méi)有傳遞b
時(shí)佳遂,b
為0
营袜,當(dāng)沒(méi)有傳遞c
時(shí),c
為 1
丑罪。
我們可以這樣調(diào)用sum
函數(shù):
sum(2) # 只傳遞了 a荚板,b 和 c 都是默認(rèn)值
sum(2, 3) # 傳遞了 a 和 b,c 是默認(rèn)值
sum(2, 3, 5) # a吩屹,b跪另,c 都傳遞了
sum(a=2, b=5) # 傳遞參數(shù)時(shí),普通參數(shù)和默認(rèn)參數(shù)都可以加上參數(shù)名
sum(2, c=7) # 傳遞了a 和 c煤搜,b 是默認(rèn)值
sum(2, b=2, c=7) # 傳遞默認(rèn)參數(shù)時(shí)免绿,可以加上參數(shù)名
sum(c=2, a=2, b=7) # 傳遞參數(shù)時(shí),如果有參數(shù)名擦盾,參數(shù)的順序可以隨意
需要注意的是:
- 傳遞參數(shù)時(shí)嘲驾,普通參數(shù)和默認(rèn)參數(shù)都可以加上參數(shù)名
- 傳遞參數(shù)時(shí),如果有寫(xiě)參數(shù)名迹卢,參數(shù)的順序可以隨意
- 傳遞參數(shù)時(shí)辽故,如果沒(méi)有寫(xiě)參數(shù)名,參數(shù)的順序必須按照函數(shù)定義的順序來(lái)寫(xiě)
默認(rèn)參數(shù)不要是可變類(lèi)型
默認(rèn)參數(shù)可以是任意類(lèi)型的數(shù)據(jù)婶希,但是建議不要是可變類(lèi)型數(shù)據(jù)榕暇,否則容易出現(xiàn)不可預(yù)料的結(jié)果。
比如代碼:
def test(l=[]):
l.append(1)
return l
當(dāng)調(diào)用該函數(shù)時(shí)喻杈,如果傳遞了參數(shù)彤枢,這種情況下一般不會(huì)出現(xiàn)什么問(wèn)題:
>>> test([])
[1]
>>> test([2])
[2, 1]
但是,如果沒(méi)有傳遞參數(shù)筒饰,而是使用的默認(rèn)參數(shù)缴啡,結(jié)果可能不是你想象的:
>>> test()
[1]
>>> test()
[1, 1]
>>> test()
[1, 1, 1]
如上代碼,我們連續(xù)調(diào)用了三次test()
瓷们,都使用的是默認(rèn)參數(shù)业栅。
第一次調(diào)用似乎還是正常的,但后兩次的結(jié)果顯示谬晕,函數(shù)返回的結(jié)果碘裕,應(yīng)該是累計(jì)了前邊調(diào)用。
這是因?yàn)樵芮?dāng)使用默認(rèn)參數(shù)時(shí)帮孔,函數(shù)記錄了默認(rèn)參數(shù)的一開(kāi)始的變量地址
,這個(gè)變量地址始終是不會(huì)改變的,但是如果這個(gè)變量類(lèi)型是可變類(lèi)型
的數(shù)據(jù)(比如例子中的列表類(lèi)型)文兢,那么其中的值是有可能改變的晤斩。
這就是出現(xiàn)以上情況的原因,所以姆坚,建議在使用默認(rèn)參數(shù)時(shí)澳泵,不要使用可變類(lèi)型的參數(shù),容易出問(wèn)題兼呵。
5兔辅,不定長(zhǎng)參數(shù)
當(dāng)你不知道函數(shù)的參數(shù)有幾個(gè)的時(shí)候,你可以使用不定長(zhǎng)參數(shù)萍程。
不定長(zhǎng)參數(shù)的格式幢妄,是在普通參數(shù)之前加一個(gè)星號(hào)*
,如下:
def print_num(*num):
print(num)
此時(shí)茫负,在函數(shù)內(nèi)部蕉鸳,num
實(shí)際上是一個(gè)元組
類(lèi)型。所以忍法,我們以元組的方式來(lái)操作num
即可潮尝。
我們可以給該函數(shù)傳遞不同個(gè)數(shù)
的參數(shù):
print_num(1) # 傳遞了 1 個(gè)參數(shù),num = (1)
print_num(1, 2) # 傳遞了 2 個(gè)參數(shù)饿序,num = (1, 2)
print_num(1, 2, 3) # 傳遞了 3 個(gè)參數(shù)勉失,num = (1, 2, 3)
有名參數(shù)
不定長(zhǎng)的有名參數(shù)
,是在普通參數(shù)之前加兩個(gè)星號(hào)**
原探,如下:乱凿、
def print_num(**num):
print(num)
此時(shí),在函數(shù)內(nèi)部咽弦,num
實(shí)際上是一個(gè)字典
類(lèi)型徒蟆。所以,我們以字典的方式來(lái)操作num
即可型型。
我們可以給該函數(shù)傳遞不同個(gè)數(shù)
的有名參數(shù)
段审,參數(shù)名
被解析成了字符串類(lèi)型:
print_num(a=1) # 傳遞了 1 個(gè)參數(shù),num = {'a': 1}
print_num(a=1, b=2) # 傳遞了 2 個(gè)參數(shù)闹蒜,num = {'a': 1, 'b': 2}
各種參數(shù)的順序
如果一個(gè)函數(shù)中各種參數(shù)都有的話(huà)寺枉,它們的順序是有要求的:如下:
def 函數(shù)名(普通參數(shù),默認(rèn)參數(shù)绷落,不定長(zhǎng)參數(shù)姥闪,不定長(zhǎng)有名參數(shù)):
# 函數(shù)體
...
建議:
不要使用太多的參數(shù)組合,不便于理解
6砌烁,函數(shù)作為參數(shù)
由于函數(shù)
也是一種數(shù)據(jù)類(lèi)型筐喳,所以函數(shù)也可以作為函數(shù)參數(shù)
傳遞給函數(shù),然后在函數(shù)內(nèi)部,再調(diào)用該函數(shù)疏唾。
def sum(a, b):
return a + b
def test(fun):
n = fun(1, 2)
print(n)
test(sum)
上面代碼中,sum
與 test
都是函數(shù)函似。
sum()
函數(shù)槐脏,計(jì)算兩個(gè)參數(shù)之和,并將結(jié)果返回撇寞。
test()
函數(shù)顿天,接收一個(gè)參數(shù) fun
,該參數(shù)是一個(gè)函數(shù)類(lèi)型蔑担。在函數(shù)體內(nèi)牌废,用代碼n = fun(1, 2)
進(jìn)行調(diào)用。
代碼test(sum)
啤握,將函數(shù)sum
作為參數(shù)傳遞給test()
鸟缕。
以上就是函數(shù)
作為函數(shù)參數(shù)
的用法。
7排抬,函數(shù)返回值
函數(shù)可以有返回值懂从,也可以沒(méi)有返回值。
函數(shù)的返回值蹲蒲,需要用到return
關(guān)鍵字番甩,當(dāng)函數(shù)的執(zhí)行遇到return
時(shí),函數(shù)的執(zhí)行就會(huì)結(jié)束届搁,并且返回一個(gè)值缘薛。
如果一個(gè)函數(shù)沒(méi)有調(diào)用return
,那么在函數(shù)執(zhí)行完畢后卡睦,會(huì)默認(rèn)返回None
:
def test():
print('hello python.')
# 沒(méi)有使用 return
ret = test() # ret 為 None
如果一個(gè)函數(shù)調(diào)用了return
宴胧,但是return
后邊沒(méi)有值,那么此時(shí)的含義是么翰,表示函數(shù)立馬返回牺汤,且返回值為None
:
def test():
print('hello python.')
return
# return 后邊的語(yǔ)句永遠(yuǎn)不會(huì)被執(zhí)行到
print('hello java.')
ret = test() # ret 為 None
你可以在return
后邊跟一個(gè)任意類(lèi)型的值,作為函數(shù)的返回值:
def test(flag):
if flag:
return True # 返回 True
else:
return False # 返回 False
ret = test(True) # ret 為 True
ret = test(False) # ret 為 False
函數(shù)返回多個(gè)值
函數(shù)不僅可以返回一個(gè)值浩嫌,也可以返回多個(gè)值:
def test(a, b, c):
return a, b, c
實(shí)際上這個(gè)時(shí)候檐迟,a, b, c
被自動(dòng)打包
到了一個(gè)元組里邊,我們用一個(gè)變量去接收返回值:
ret = test(1, 2, 3)
print(ret) # ret 為 (1, 2, 3)
print(type(ret)) # <class 'tuple'>
我們也可以用三個(gè)變量去接收這個(gè)返回值码耐,這時(shí)返回值會(huì)被自動(dòng)解包
:
a, b, c = test(1, 2, 3)
# a 為 1
# b 為 2
# c 為 3
自動(dòng)解包時(shí)追迟,如果變量的個(gè)數(shù)與返回值的個(gè)數(shù)不同,會(huì)拋出ValueError
異常:
a, b = test(1, 2, 3) # 變量少于返回值骚腥,拋出異常
a, b, c, d = test(1, 2, 3) # 變量多于返回值敦间,拋出異常
8,匿名函數(shù)
Python 中的匿名函數(shù)使用 lambda
關(guān)鍵字,也稱(chēng)為lambda
表達(dá)式廓块。
lambda
表達(dá)式雖然也是函數(shù)
厢绝,但是一般只包含簡(jiǎn)單的邏輯,它是普通函數(shù)的一種簡(jiǎn)寫(xiě)形式带猴。一般常作為函數(shù)參數(shù)昔汉,傳遞給函數(shù)。
格式如下:
lambda 參數(shù)列表 : 表達(dá)式
lambda
后跟一個(gè)參數(shù)列表拴清,然后是一個(gè)冒號(hào):
靶病,最后是一個(gè)表達(dá)式,表達(dá)式的計(jì)算結(jié)果會(huì)作為匿名函數(shù)的返回值
(不需要用return
)口予。
可以將lambda
表達(dá)式賦值給一個(gè)變量娄周,格式如下:
變量名 = lambda 參數(shù)列表 : 表達(dá)式
示例:
sum = lambda x, y : x + y
該匿名函數(shù)接收兩個(gè)參數(shù)x
,y
沪停,然后計(jì)算結(jié)果 x + y
煤辨,并將結(jié)果返回。
通過(guò)type(sum)
查看其類(lèi)型:
>>> type(sum)
<class 'function'>
上面這個(gè)lambda
表達(dá)式就相當(dāng)于如下函數(shù):
def sum(x, y):
return x + y
我們可以像調(diào)用普通函數(shù)一樣牙甫,調(diào)用該lambda
表達(dá)式:
>>> sum(1, 2) # 返回 1 + 2
3
>>> sum(2, 3) # 返回 2 + 3
5
匿名函數(shù)的應(yīng)用場(chǎng)景
在介紹Python 列表的時(shí)候掷酗,我們介紹過(guò)一個(gè)L.sort()
函數(shù),該函數(shù)的原型如下:
L.sort(key=None, reverse=False) -> None
其中的key
參數(shù)就是一個(gè)函數(shù)類(lèi)型參數(shù)窟哺,這種情況下就可以使用匿名函數(shù)泻轰。如下:
>>> l.sort(key = lambda x: x[0])
>>> l
[('a', 3), ('b', 2), ('c', 1)]
>>>
>>> l.sort(key = lambda x: x[1])
>>> l
[('c', 1), ('b', 2), ('a', 3)]
(完。)
推薦閱讀:
Python 簡(jiǎn)明教程 ---10且轨,Python 列表
Python 簡(jiǎn)明教程 ---11浮声,Python 元組
Python 簡(jiǎn)明教程 ---12,Python 字典
Python 簡(jiǎn)明教程 ---13旋奢,Python 集合
Python 簡(jiǎn)明教程 ---14泳挥,Python 數(shù)據(jù)結(jié)構(gòu)進(jìn)階