1.函數(shù)的介紹
為什么要有函數(shù)?因為在平時寫代碼時,如果沒有函數(shù)的話,那么將會出現(xiàn)很多重復的代碼,這樣代碼重用率就比較低。。。并且這樣的代碼維護起來也是很有難度的,為了解決這些問題,就出現(xiàn)了函數(shù),用來將一些經(jīng)常出現(xiàn)的代碼進行封裝,這樣就可以在任何需要調(diào)用這段代碼的地方調(diào)用這個函數(shù)就行了。
函數(shù)的定義:函數(shù)是指將一組語句的集合通過一個名字(函數(shù)名)封裝起來,要想執(zhí)行這個函數(shù),只需調(diào)用其函數(shù)名即可
特性:
a.代碼重用
b.保持一致性
c.可擴展性
2.函數(shù)的創(chuàng)建
在python中函數(shù)定義的格式如下:
函數(shù)的調(diào)用使用 :函數(shù)名(實參) 就可以調(diào)用函數(shù)了。
函數(shù)名的命名規(guī)則和變量的命名規(guī)則一樣:
1、函數(shù)名必須以下劃線或字母開頭,可以包含任意字母、數(shù)字或下劃線的組合。不能使用任何的標點符號;
2、函數(shù)名是區(qū)分大小寫的。
3、函數(shù)名不能是保留字。
形參和實參的區(qū)別:
函數(shù)在定義的時候,函數(shù)名后面的括號中可以添加參數(shù),這些參數(shù)就叫做形參,形參:顧名思義就是形式參數(shù),只是一個代號。
實參是在調(diào)用函數(shù)的時候函數(shù)名后面的括號中的參數(shù),形參和實參需要一一對應起來,否則調(diào)用函數(shù)會報錯。
3.函數(shù)參數(shù)及返回值
前面提到函數(shù)的形參和實參要一一對應,那么參數(shù)對應有如下幾種:
1、必須參數(shù)
2、關鍵字參數(shù)
3、默認參數(shù)
4、不定長參數(shù) *args
5、不定長參數(shù) **kwargs
1.必須參數(shù):
必須參數(shù)必須以對應的關系一個一個傳遞進入函數(shù),函數(shù)調(diào)用時傳遞的實參必須和函數(shù)定義時的形參一一對應,不能多也不能少,順序也得一致。
舉個栗子:
2.關鍵字參數(shù)
關鍵字參數(shù)是實參里面的概念,在調(diào)用函數(shù)的時候聲明某個參數(shù)是屬于某個關鍵字的。使用關鍵字參數(shù)允許函數(shù)調(diào)用時參數(shù)的順序與聲明時不一致,因為 Python 解釋器能夠用參數(shù)名匹配參數(shù)值。
舉個栗子:
3.默認參數(shù)
默認參數(shù)是在函數(shù)聲明的時候,可以給某個參數(shù)指定默認值,這樣的參數(shù)叫做默認值參數(shù)。如果在調(diào)用函數(shù)的時候,默認參數(shù)沒有接收到對應的實參,那么就會將默認值賦值給這個參數(shù)。
4.不定長參數(shù) *args
在python里面,函數(shù)在聲明的時候,參數(shù)中可以使用(*變量名)的方式來接受不確定長度的參數(shù),但是在python里面大家約定俗成使用*args接受不定長參數(shù),這樣在調(diào)用函數(shù)的時候傳遞的參數(shù)就可以是不定長度的了。args接受了不定長參數(shù)之后,將這些參數(shù)放到一個tuple里面,可以通過訪問args來獲取這些不定長參數(shù)。
5.不定長參數(shù) **kwargs
但是上面的args只能接收未命名的參數(shù),那假如有類似于關鍵字參數(shù)的不定長參數(shù)該怎么辦呢?python里面使用(**變量名)來接收不定長的命名變量參數(shù)。同樣,python里面也約定俗成使用**kwargs接收不定長命名參數(shù)。kwargs接收了不定長參數(shù)之后,將這些參數(shù)放到一個字典里面,可以通過key獲取到相應的參數(shù)值。
介紹完了這些參數(shù)之后,接下來要介紹的是關于這些參數(shù)混合使用的情況:
假如一個函數(shù)使用了上面所有種類的參數(shù),那該怎么辦?為了不產(chǎn)生歧義,python里面規(guī)定了假如有多種參數(shù)混合的情況下,遵循如下的順序使用規(guī)則:
def f(必須參數(shù),默認參數(shù),*args,**kwargs):
????pass
如果同時存在args和kwargs的話,args在左邊
默認參數(shù)在必須參數(shù)的右邊,在*args的左邊
關鍵字參數(shù)的位置不固定(ps:關鍵字參數(shù)也不在函數(shù)定義的時候確定)
那么,假如有一個列表想要傳遞進入一個不定長的未命名參數(shù)的函數(shù)中去,可以在該列表前面加上*實現(xiàn),同理如果想傳遞一個字典進入不定長命名參數(shù)的函數(shù)中去,可以在該字典前面加上**
舉個栗子:
deff(*args,**kwargs):
????print(args)
????for i in kwargs:
print("%s:%s"%(i,kwargs[i]))
f(*[1,2,3],**{"a":1,"b":2})
函數(shù)的返回值
要想獲取函數(shù)的執(zhí)行結果,就可以用return語句把結果返回
注意:
函數(shù)在執(zhí)行過程中只要遇到return語句,就會停止執(zhí)行并返回結果,也可以理解為 return 語句代表著函數(shù)的結束 如果未在函數(shù)中指定return,那這個函數(shù)的返回值為None
return多個對象,解釋器會把這多個對象組裝成一個元組作為一個一個整體結果輸出。
4.LEGB作用域
python中的作用域分4種情況:
L:local,局部作用域,即函數(shù)中定義的變量;
E:enclosing,嵌套的父級函數(shù)的局部作用域,即包含此函數(shù)的上級函數(shù)的局部作用域,但不是全局的;
G:globa,全局變量,就是模塊級別定義的變量;
B:built-in,系統(tǒng)固定模塊里面的變量,比如int, bytearray等。 搜索變量的優(yōu)先級順序依次是:作用域局部>外層作用域>當前模塊中的全局>python內(nèi)置作用域,也就是LEGB。
local和enclosing是相對的,enclosing變量相對上層來說也是local。
在Python中,只有模塊(module),類(class)以及函數(shù)(def、lambda)才會引入新的作用域,其它的代碼塊(如if、try、for等)不會引入新的作用域。
global關鍵字
當內(nèi)部作用域想修改外部作用域的變量時,就要用到global和nonlocal關鍵字了,當修改的變量是在全局作用域(global作用域)上的,就要使用global先聲明一下。
nonlocal關鍵字
global關鍵字聲明的變量必須在全局作用域上,不能嵌套作用域上,當要修改嵌套作用域(enclosing作用域,外層非全局作用域)中的變量怎么辦呢,這時就需要nonlocal關鍵字。
小結
變量查找順序:LEGB,作用域局部>外層作用域>當前模塊中的全局>python內(nèi)置作用域;
只有模塊、類、及函數(shù)才能引入新作用域;
對于一個變量,內(nèi)部作用域先聲明就會覆蓋外部變量,不聲明直接使用,就會使用外部作用域的變量;
內(nèi)部作用域要修改外部作用域變量的值時,全局變量要使用global關鍵字,嵌套作用域變量要使用nonlocal關鍵字。nonlocal是python3新增的關鍵字,有了這個 關鍵字,就能完美的實現(xiàn)閉包了。
5.特殊函數(shù)
遞歸函數(shù)定義:遞歸函數(shù)就是在函數(shù)內(nèi)部調(diào)用自己
有時候解決某些問題的時候,邏輯比較復雜,這時候可以考慮使用遞歸,因為使用遞歸函數(shù)的話,邏輯比較清晰,可以解決一些比較復雜的問題。但是遞歸函數(shù)存在一個問題就是假如遞歸調(diào)用自己的次數(shù)比較多的話,將會使得計算速度變得很慢,而且在python中默認的遞歸調(diào)用深度是1000層,超過這個層數(shù)將會導致“爆棧”。。。所以,在可以不用遞歸的時候建議盡量不要使用遞歸。
遞歸函數(shù)的優(yōu)點:定義簡單,邏輯清晰。理論上,所有的遞歸函數(shù)都可以寫成循環(huán)的方式,但循環(huán)的邏輯不如遞歸清晰。
遞歸特性:
1、必須有一個明確的結束條件
2、每次進入更深一層遞歸時,問題規(guī)模相比上次遞歸都應有所減少
3、遞歸效率不高,遞歸層次過多會導致棧溢出(在計算機中,函數(shù)調(diào)用是通過棧(stack)這種數(shù)據(jù)結構實現(xiàn)的,每當進入一個函數(shù)調(diào)用,棧就會加一層棧幀,每當函數(shù)返 回,棧就會減一層棧幀。由于棧的大小不是無限的,所以,遞歸調(diào)用的次數(shù)過多,會導致棧溢出。)
6.函數(shù)式編程
關于函數(shù)式編程,我理解的也不是很深,但是python中有4個比較重要的內(nèi)置函數(shù),組合起來使用有時候能大大提高編程效率。
1、filter(function, sequence)
str=['a','b','c','d']
def fun1(s):
if s !='a':
return s
ret=filter(fun1,str)
print(list(ret))# ret是一個迭代器對象
對sequence中的item依次執(zhí)行function(item),將執(zhí)行結果為True的item做成一個filter object的迭代器返回。可以看作是過濾函數(shù)。
2、 map(function, sequence)
str=[1,2,'a','b']
def fun2(s):
return s+"alvin"
ret=map(fun2,str)
print(ret)# map object的迭代器
print(list(ret))# ['aalvin', 'balvin', 'calvin', 'dalvin']
對sequence中的item依次執(zhí)行function(item),將執(zhí)行結果組成一個map object迭代器返回. map也支持多個sequence,這就要求function也支持相應數(shù)量的參數(shù)輸入:
def add(x,y):
return x+y
print(list(map(add,range(10),range(10))))##[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
3、 reduce(function, sequence, starting_value)
from functools import reduce
def add1(x,y):
return x+y
print(reduce(add1,range(1,101)))## 4950 (注:1+2+...+99)
print(reduce(add1,range(1,101),20))## 4970 (注:1+2+...+99+20)
對sequence中的item順序迭代調(diào)用function,如果有starting_value,還可以作為初始值調(diào)用.
4 、lambda
普通函數(shù)與匿名函數(shù)的對比:
#普通函數(shù)
def add(a,b):
return a+b
print add(2,3)
#匿名函數(shù)
add=lambda a,b : a+b
print add(2,3)
#========輸出===========
5
5
匿名函數(shù)的命名規(guī)則,用lamdba 關鍵字標識,冒號(:)左側表示函數(shù)接收的參數(shù)(a,b) ,冒號(:)右側表示函數(shù)的返回值(a+b)。
因為lamdba在創(chuàng)建時不需要命名,所以,叫匿名函數(shù)