1. 函數(shù)介紹
??函數(shù) 在編程語言中就是完成特定功能的一個(gè)詞句組 (代碼塊),這組語句可以作為一個(gè)單位使用,并且給它取一個(gè)名字??梢酝ㄟ^函數(shù)名在程序的不同地方多次執(zhí)行(這叫 函數(shù)的調(diào)用)。函數(shù)在編程語言中有基本分為:預(yù)定義函數(shù),自定義函數(shù)。預(yù)定義函數(shù)可以直接使用,而自定義函數(shù)顧名思義需要我們自己定義函數(shù)。
在數(shù)學(xué)中的定義,這里就不介紹了。請參考 函數(shù)(數(shù)學(xué)函數(shù))_百度百科
1.1. 為什么要使用函數(shù)
在編程中使用函數(shù)主要有兩個(gè)優(yōu)點(diǎn):
- 降低編程難度:通常將一個(gè)復(fù)雜的大問題分解成一系列的小問題,然后將小問題劃分成更小的問題,當(dāng)問題細(xì)化為足夠簡單時(shí),我們就可以分而治之,各個(gè)小問題解決了,大問題就迎刃而解了。
- 代碼重用:避免重復(fù)勞作,提供效率
- 代碼更加簡潔美觀,可讀性增加
1.2. Python 中的函數(shù)
??在 Python 中,函數(shù)由 若干語句組成的代碼塊、函數(shù)名稱、參數(shù)列表 構(gòu)成,它是組織代碼的最小單元,使用函數(shù)可以完成一定的功能,在 Python 中函數(shù)主要分為三類:內(nèi)置函數(shù)、第三方函數(shù)庫、自定義函數(shù)。常用的內(nèi)置函數(shù)在前面已經(jīng)介紹,第三方函數(shù)庫需要先引入模塊,通過模塊調(diào)用,在模塊學(xué)習(xí)中進(jìn)行介紹,這里主要說的是如何自定義一個(gè)函數(shù)。
2. 函數(shù)的基本使用
??在 Python 中,定義一個(gè)函數(shù)要使用 def 語句,依次寫出 函數(shù)名、括號(hào)、括號(hào)中的參數(shù)和冒號(hào):,然后,在縮進(jìn)塊中編寫函數(shù)體,函數(shù)的返回值用 return 語句 返回。下面是一個(gè)函數(shù)的基本結(jié)構(gòu):
def 函數(shù)名(參數(shù)列表):
函數(shù)體(代碼塊)
[return 返回值] # 函數(shù)可以無返回值
注意:
-
函數(shù)名就是標(biāo)識(shí)符,命名要求一樣 -
語句塊必須縮進(jìn),約定4 個(gè)空格 - Python 的函數(shù)可以沒有 return 語句,會(huì)隱式地返回一個(gè) None
- 定義中的參數(shù)列表稱為
形式參數(shù),只是一種符號(hào)表達(dá)式(標(biāo)識(shí)符),簡稱形參
??我們以自定義一個(gè)求絕對值的函數(shù)為例:
In : def abs(x):
...: if x >= 0:
...: return x
...: else:
...: return -x
...:
In : abs(-10)
Out: 10
In : abs
Out: <function __main__.abs(x)>
In : print(abs)
<function abs at 0x0000027021055E18>
??上面只是一個(gè)函數(shù)的定義,具體來看一下各個(gè)部分的解釋:
- 函數(shù)名字叫做 abs,接受 1 個(gè)形式參數(shù) x。
- return x: 表示要返回的值是 x,函數(shù)可以無返回值。
- 函數(shù)是一個(gè)
可調(diào)用對象,函數(shù)名加括號(hào)就表示調(diào)用。 - 和變量的使用方式相同,在使用函數(shù)時(shí),需要預(yù)先進(jìn)行定義。
- 直接打印函數(shù)名,不會(huì)觸發(fā)函數(shù)的執(zhí)行,反而會(huì)打印函數(shù)的內(nèi)存地址。
??我們自定義的函數(shù) abs, 由于與內(nèi)置函數(shù)重名,那么將會(huì)覆蓋掉內(nèi)置函數(shù),所以請謹(jǐn)慎命名。
??函數(shù)體內(nèi)部的語句在執(zhí)行時(shí),一旦執(zhí)行到 return 時(shí),函數(shù)就執(zhí)行完畢,并將結(jié)果返回。因此,函數(shù)內(nèi)部通過條件判斷和循環(huán)可以實(shí)現(xiàn)非常復(fù)雜的邏輯。如果沒有 return 語句,函數(shù)執(zhí)行完畢后會(huì)隱式地返回 None。如果我們確實(shí)要 return None 可以簡寫為 return,或者不寫 return 語句 (會(huì)讓人看不懂,建議只寫 return)。
??調(diào)用函數(shù),那么只需要使用函數(shù)名加括號(hào),就能執(zhí)行,但如果函數(shù)定義了參數(shù),那么必須在執(zhí)行的時(shí)候傳遞參數(shù)給函數(shù),否則會(huì)報(bào)異常!關(guān)于調(diào)用我們還需要了解:
- 函數(shù)定義,只是聲明了一個(gè)函數(shù),它不會(huì)被執(zhí)行,調(diào)用時(shí)才會(huì)執(zhí)行
- 調(diào)用方式就是函數(shù)名后加上小括號(hào),如果有必要需要在括號(hào)內(nèi)寫上參數(shù)
- 調(diào)用時(shí)寫的參數(shù)是實(shí)際參數(shù),是實(shí)實(shí)在在傳入的值,簡稱實(shí)參
函數(shù)是可調(diào)用對象,可以使用 callable() 進(jìn)行判斷
In : def abs(x):
...: if x >= 0:
...: return x
...: else:
...: return -x
# 函數(shù)是可調(diào)用的,注意這里不能對函數(shù)加括號(hào),那么 callable 判斷的就是函數(shù)的返回值是否可以執(zhí)行了
In : callable(abs)
Out: True
# 字符串是不可調(diào)用的
In : a = '123'
In : callable(a)
Out: False
3. 函數(shù)的參數(shù)
??定義函數(shù)的時(shí)候,括號(hào)中指定的就是函數(shù)的參數(shù) (形式參數(shù)),當(dāng)我們調(diào)用函數(shù)時(shí),需要將數(shù)據(jù)進(jìn)行傳遞,這種傳遞參數(shù)的方式就叫做傳參,嚴(yán)格來說函數(shù)只可以使用兩種方式:位置傳參 和 關(guān)鍵字傳參。
-
位置傳參:按照參數(shù)定義順序傳入實(shí)參,位置參數(shù)是按位置對應(yīng)的 -
關(guān)鍵字傳參:使用形參的名字來傳入實(shí)參的方式,如果使用了形參的名字,那么傳入?yún)?shù)的順序就可以和定義的順序不同 - 當(dāng)
位置傳參和關(guān)鍵字傳參混用時(shí),位置參數(shù)必須在關(guān)鍵字參數(shù)之前傳入。
# 定義函數(shù) function:
In : def function(x, y):
...: result = x + y
...: return result
...:
# 位置傳參: # 1 對應(yīng) x,2 對應(yīng) y。
In : function(1, 2)
Out: 3
# 關(guān)鍵字傳參: 使用關(guān)鍵字時(shí),參數(shù)可以不用按照位置順序。
In : function(y=1, x=2)
Out: 3
# 混合傳參: 混用時(shí),關(guān)鍵字參數(shù)必須要再位置參數(shù)右邊
In : function(1, y = 3)
Out: 4
3.1. 參數(shù)的缺省值
??在定義形式參數(shù)時(shí),為參數(shù)指定對應(yīng)的值,就叫做參數(shù)的缺省值,當(dāng)定義了參數(shù)的缺省值以后,我們傳參時(shí)可以選擇傳遞該參數(shù)的值,也可以選擇不傳遞,當(dāng) 不傳遞 此參數(shù)的值時(shí),該參數(shù)就使用指定的 缺省值,否則 將會(huì)使用 傳遞的值。
參數(shù)缺省值也屬于位置參數(shù),只不過是給位置參數(shù)定義了默認(rèn)值。
In : def function(x = 10, y = 20):
...: return x + y
...:
# 2 會(huì)以位置參數(shù)的形式傳遞給 x,y 沒有傳遞,會(huì)使用缺省值
In : function(2)
Out: 22
# x 沒有傳遞,會(huì)使用缺省值
In : function(y=100)
Out: 110
# y 沒有傳遞,會(huì)使用缺省值
In : function(x=100)
Out: 120
# 20 以位置參數(shù)的形式傳遞給 x,500 以關(guān)鍵字參數(shù)的形式傳遞給了 y
In : function(20, y=500)
Out: 520
當(dāng)定義參數(shù)的缺省值時(shí),注意
缺省值參數(shù)必須要放在位置參數(shù)的右邊
# 必須把 x=1,放在 y 的右邊,否則無法完成函數(shù)定義
In : def functions(x=1, y):
...: print(x+y)
File "<ipython-input-1-ea496fa7fc81>", line 1
def functions(x=1, y):
^
SyntaxError: non-default argument follows default argument
使用缺省值參數(shù)的好處是:
- 參數(shù)的缺省值可以在未傳入足夠的實(shí)參的時(shí)候,對沒有給定的參數(shù)賦值為缺省值。
- 參數(shù)非常多的時(shí)候,并不需要用戶每次都輸入所有的參數(shù),簡化函數(shù)調(diào)用。
3.2. 可變參數(shù)
??可變參數(shù)顧名思義表示參數(shù)的數(shù)量是可變的,并且可以使用一個(gè)形參匹配任意個(gè)實(shí)參。針對傳遞參數(shù)方式的不同又分為 可變位置傳參 和 可變關(guān)鍵字傳參。
3.2.1. 可變位置傳參
??在形參前使用 * 號(hào),表示該形參是可變參數(shù),可以接受多個(gè)實(shí)參,在函數(shù)內(nèi)部,可變參數(shù)會(huì)封裝成元組 (即便是沒有傳遞)
In : def function(*nums):
...: print(nums)
...:
In : function(1,2,3,4) # 多個(gè)參數(shù)會(huì)被 nums 收集
(1, 2, 3, 4)
In : function([1,2,3,4]) # 會(huì)把 list 收集成元組內(nèi)的一個(gè)元素
([1, 2, 3, 4],)
在函數(shù)定義時(shí),一般的規(guī)范是使用
*args, 表示收集多個(gè)位置傳參。
3.2.2. 可變關(guān)鍵字傳參
??在形參前使用 ** 號(hào),表示該形參是可變關(guān)鍵字參數(shù),可以接受多個(gè)關(guān)鍵字參數(shù),在函數(shù)內(nèi)部,可變關(guān)鍵字參數(shù)會(huì)封裝成字典 (即便是沒有傳遞)
In : def function(**kwargs):
...: print(kwargs)
...:
In : function(a=1,b=2)
{'a': 1, 'b': 2}
# 參數(shù)的缺省值和位置參數(shù)同時(shí)使用
In : def function(x=1, y=2, **kwargs):
...: print('x = {}'.format(x))
...: print('y = {}'.format(y))
...: print(kwargs)
...:
# 參數(shù)沒有 a,b 形參,則被 kwargs 接收
In : function(a=10, b=20)
x = 1
y = 2
{'a': 10, 'b': 20}
# z 沒有被匹配,被 kwargs 接收
In : function(x=100,y=200,z=300)
x = 100
y = 200
{'z': 300}
# 前面的按照位置參數(shù)賦值,z 同樣被字典 kwargs 收集
In : function(1000,2000,z=300)
x = 1000
y = 2000
{'z': 300}
3.2.3. 可變參數(shù)混合使用
??前面說的各種參數(shù)是可以混合使用的,當(dāng)混合使用時(shí)遵循一定的順序,簡單總結(jié)一下,按照從左至右的順序來說:位置參數(shù),缺省值參數(shù),可變位置參數(shù),可變關(guān)鍵字參數(shù):
def function(位置參數(shù),缺省值參數(shù),可變位置參數(shù),可變關(guān)鍵字參數(shù)):
??無論如何,順序不能顛倒。
# x,y,z 都為位置參數(shù),z 的缺省值為 1,*args 收集額外的位置傳參,**kwargs 收集額外的關(guān)鍵字傳參
In : def function(x,y,z=1,*args,**kwargs):
...: print(x,y,z)
...: print(args)
...: print(kwargs)
...:
In : function(1,2,3,4,5,a=1,b=2)
1 2 3
(4, 5)
{'a': 1, 'b': 2}
3.2.4. 可變參數(shù)小結(jié)
??針對可變參數(shù)以及不同的混用方式可以有如下結(jié)論:
- 分為
可變位置參數(shù)和可變關(guān)鍵字參數(shù) -
可變位置參數(shù)在形參前使用一個(gè)星號(hào) * -
可變關(guān)鍵字參數(shù)在形參前使用兩個(gè)星號(hào) ** -
可變位置參數(shù)和可變關(guān)鍵字參數(shù)都可以收集若干個(gè)實(shí)參,可變位置參數(shù)收集形成一個(gè)tuple,可變關(guān)鍵字參數(shù)收集形成一個(gè)dict - 混合使用參數(shù)的時(shí)候,在定義階段要遵循如下順序:
位置參數(shù),缺省值參數(shù),可變位置參數(shù),可變關(guān)鍵字參數(shù)
??當(dāng)位置傳參和關(guān)鍵字傳參同時(shí)使用時(shí),針對同一個(gè)形參不可以重復(fù)賦值!,這一點(diǎn)使用的時(shí)候很重要。
3.3. keyword-only 參數(shù) *
??Python3 的函數(shù)參數(shù)中,新增了 keyword-only 參數(shù),什么叫 keyword-only 參數(shù)? 當(dāng)在一個(gè)星號(hào)參數(shù) (可變位置參數(shù)) 后,出現(xiàn)的普通參數(shù),我們稱它為 keyword-only 參數(shù),因?yàn)槎嘤嗟奈恢脜?shù)都會(huì)被 *args 收集,只能通過 keyword 的方式對這些形參進(jìn)行賦值,所以它們只能使用 關(guān)鍵字傳參。
# x,y 是 keyword-only 參數(shù),其中 y 存在缺省值,可以不用傳遞,x 必須使用關(guān)鍵字的方式進(jìn)行傳遞
In : def function(a, b, c=1, *args, x, y=2, **kwargs):
...: print(a,b,c)
...: print(args)
...: print(x,y)
...: print(kwargs)
...:
In : function(100,200,300,400,d=100,e=200)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-cf60009e3d1f> in <module>
----> 1 function(100,200,300,400,d=100,e=200)
TypeError: function() missing 1 required keyword-only argument: 'x'
In : function(100,200,300,400,d=100,e=200,x=500)
100 200 300
(400,)
500 2
{'d': 100, 'e': 200}
特殊形式:
In : def function(*, x, y):
...: print(x,y)
...:
In : function(1, 2, 3, x=100, y=200)
---------------------------------------------------------------------------
TypeError Traceback(most recent call last)
<ipython-input-18-7d07ae79c088> in <module>
----> 1 function(1,2,3,x=100,y=200)
TypeError: function() takes 0 positional arguments but 3 positional arguments(and 2 keyword-only arguments) were given
In : function(x=100, y=200)
100 200
這里 * 表示不接受位置傳參,只能使用關(guān)鍵字對參數(shù)進(jìn)行賦值
??注意: 使用了 keyword-only 參數(shù),那么在定義形參時(shí)的順序就有所改變了,它們是:位置參數(shù),缺省值參數(shù),可變位置參數(shù),keyword-only 參數(shù),可變關(guān)鍵字參數(shù)
4.1. 參數(shù)解構(gòu)
前面我們說過 Python 的封裝與結(jié)構(gòu),這里的參數(shù)也可以利用這種思想進(jìn)行解構(gòu),現(xiàn)有如下函數(shù):
In : def add(x=1, y=2):
...: print(x+y)
...:
# 將元組的元素 1 和元素 2 分別傳給 x,y
In : t = (10,20)
In : add(t[0], t[1])
30
# 非字典類型使用 * 解構(gòu)成位置參數(shù)
In : add(*t)
30
# 字典型使用 ** 解構(gòu)成關(guān)鍵字參數(shù)
In : d = {'x':100, 'y':200}
In : add(**d)
300
- 將 t 在傳遞參數(shù)時(shí)解構(gòu)為
10,20,作為位置傳參傳遞給 add 函數(shù) - 將 d 在傳遞參數(shù)時(shí)解構(gòu)為
x=100,y=200,作為關(guān)鍵字傳參傳遞給函數(shù) - 這種方法在后面函數(shù)的調(diào)用過程中非常常用
??現(xiàn)在再來回頭看一下,什么時(shí)參數(shù)解構(gòu)?
- 給函數(shù)提供實(shí)參的時(shí)候,可以在集合類型前使用
*或者**,把集合類型的結(jié)構(gòu)解開,提取出其中所有元素作為函數(shù)的實(shí)參 - 非字典類型使用
* 解構(gòu)成位置參數(shù) - 字典型使用
** 解構(gòu)成關(guān)鍵字參數(shù) - 提取出來的元素?cái)?shù)目要和參數(shù)的要求匹配,也要和參數(shù)的類型匹配,否則請使用
*args,**kwargs
In : def add(a,b,*args,m,n,**kwargs):
...: print(a + b + m + n)
...:
In : dic = {'a':100,'b':200}
In : add(**dic,m=300,n=400,x=1000)
1000
5. 函數(shù)的返回值
??我們通常編寫函數(shù)除了代碼可以復(fù)用,更多的時(shí)候需要的是知道函數(shù)的運(yùn)算結(jié)果,函數(shù)把運(yùn)算的結(jié)果返回給我們,這個(gè)結(jié)果就叫作做函數(shù)的返回值。使用 return 關(guān)鍵字進(jìn)行返回。
# 返回 1 個(gè)數(shù)據(jù)
In : def add(x,y):
...: result = x + y
...: return result
...:
In : a = add(10,20)
In : print(a)
30
# 返回多個(gè)數(shù)據(jù)
In : def add(x,y):
...: result = x + y
...: test = x * y
...: return result,test
...:
In : a = add(10,20)
In : print(a)
(30, 200)
# 多個(gè) return 語句
In : def bigger(x,y):
...: if y > x:
...: return y
...: else:
...: return x
...:
In : a = bigger(10,20)
In : print(a)
20
總結(jié):
- Python 函數(shù)使用
return語句返回 "返回值" - 所有函數(shù)都有返回值,如果沒有
return語句,隱式調(diào)用return None -
return語句并不一定是函數(shù)的語句塊的最后一條語句 - 一個(gè)函數(shù)可以存在多個(gè)
return語句,但是只有一條可以被執(zhí)行,如果沒有一條return語句被執(zhí)行,隱式調(diào)用return None
5.return None可以簡寫為 return - 函數(shù)執(zhí)行到
return就會(huì)返回,所以return后面的語句永遠(yuǎn)不會(huì)被執(zhí)行 -
return可以理解為,結(jié)束當(dāng)前函數(shù)計(jì)算,返回值!
函數(shù)永遠(yuǎn)只能返回一個(gè)數(shù)據(jù)。
- 返回值數(shù)量 = 0,即
return(或者不寫return語句),返回的數(shù)據(jù)為None。 - 返回值數(shù)量 = 1,返回 object(一個(gè)對象)。
- 返回值數(shù)量 > 1,返回 tuple(一個(gè)元組)。