python3.5修煉手冊12
函數
函數是組織好的,可重復使用的,用來實現單一或相關聯功能的代碼。
定義函數
python支持自定義函數,即由我們自己定義一個實現某個功能的函數。
自定義函數規則:
①函數代碼塊以def關鍵字開頭,後接函數標識符名稱和圓括號"()"。
②所有傳入的參數和自定義變量都必須放在圓括號中,可以在圓括號中定義參數。
③函數的第一行語句可以選擇性的使用文檔字符串,用於存放函數說明。
④函數內容以冒號開始,並且要縮進。
⑤return[表達式]結束函數,選擇性的返回一個值給調用方。不帶表達式的return相當於返回None
python 定義函數使用def關鍵字,一般格式如下;
def 函數名 (參數列表):
函數體
或者更直觀的表示為:
def<name>(arg1,arg2,...argN):
<statements>
函數的名字必須是以字母開頭,可以包括下劃線"_"。和定義變量,不能把python的關鍵字定義成函數的名字。函數內的語句數量是任意的,每個語句至少有一個空格的縮進,以表示該語句屬於這個函數。函數體必須保持縮進一致,因為在函數中,縮進結束就表示函數結束。
例如:
def hello(): print(‘hello,world‘) #調用函數 hello()
C:\python\python.exe C:/python.py/hanshu.py
hello,world
上例中的hello()就是自定義的函數,在函定義完成後做了函數自我調用。如果不調用就沒有任何輸出。
需要註意以下幾點:
(1)沒有return語句時,函數執行完畢也會返回結果,不過結果為None。
(2)return None 可以簡寫為return。
(3)在python中定義函數時,需要保持函數體中同一層級的代碼縮進一致。
在一個函數中可以輸出多條語句,並能做相應的運算操作,以及輸出運算結果。
例如:
def printmore(): print(‘該函數可以輸出多條語句,我是第一條。‘) print(‘我是第二條‘) print(‘我是第三條‘) #調用函數 printmore()
C:\python\python.exe C:/python.py/hanshu.py
該函數可以輸出多條語句,我是第一條。
我是第二條
我是第三條
定義輸出數字和計算的函數並執行。
例如:
def mixoperation(): a=10 b=20 print(a) print(b) print(a+b) print(‘a+b 的和等於:‘,a+b) #調用函數 mixoperation()
C:\python\python.exe C:/python.py/hanshu.py
10
30
a+b的和等於: 30
定義空函數可以使用pass語句。
例如:
def donothing(): pass donothing()
C:\python\python.exe C:/python.py/hanshu.py
執行結果為沒有任何輸出。
函數的目的是把一些復雜操作隱藏起來,用於簡化程序結構,使程序更容易閱讀。函數在調用前必須先定義。
函數的參數
調用函數時可以使用一下參數類型:
(1)必須是函數。
(2)關鍵字是參數。
(3)默認是參數。
(4)可變參數。
(5)組合參數。
必須參數
必須參數必須以正確的順序傳入函數。調用時數量必須和聲明時一樣。
例如:
def paramone(str): print(‘the param is‘,str) print(‘我是一個傳入參數,我的值是:‘,str) paramone(‘hello,world‘)
C:\python\python.exe C:/python.py/hanshu.py
the param is hello,world
我是一個傳入參數,我的值是: hello,world
上例定義了一個必須傳入一個參數的函數paramone(str),傳入的參數為str,結果是將‘hello,world‘傳給str。
註意:paramone()函數,不傳入參數或傳入一個以上參數,都會報錯。所以對此類函數,必須傳遞對應正確個數的參數。
關鍵字參數
關鍵字參數和函數調用關系緊密,函數調用使用關鍵字參數確定傳入的參數值。
使用關鍵字參數允許調用函數時參數的順序與聲明時不一致,因為python解釋器能夠用參數名匹配參數值。
def personinfo(age,name): print(‘年齡:‘,age) print(‘名稱:‘,name) return print(‘-----------按參數順序傳入參數--------------‘) personinfo(18,‘徐偉‘) print(‘-----------按參數順序傳入參數,指定參數名--------------‘) personinfo(name=‘徐偉‘,age=18) print(‘-----------參數順序傳入參數,指定參數名--------------‘) personinfo(age=18,name=‘徐偉‘)
C:\python\python.exe C:/python.py/hanshu.py
-----------按參數順序傳入參數--------------
年齡: 18
名稱: 徐偉
-----------按參數順序傳入參數,指定參數名--------------
年齡: 18
名稱: 徐偉
-----------參數順序傳入參數,指定參數名--------------
年齡: 18
名稱: 徐偉
由上例可以看出對於關鍵字參數personinfo()函數,只要指定參數名,輸入參數的順序對結果就沒有影響,都能得到正確的結果。
默認參數
調用函數時,如果沒有傳遞參數,就會使用默認參數。
使用默認參數,就是在定義函數時,給參數一個默認值。如果沒有給調用函數的參數賦值,調用的函數就會使用這個默認值。
例如:
def defaultparam(name,age=23): print(‘hi,我叫:‘,name) print(‘我今年:‘,age) return defaultparam(‘徐偉‘)
C:\python\python.exe C:/python.py/hanshu.py
hi,我叫: 徐偉
我今年: 23
從上例可以看出,在函數調用時沒有對age賦值,在輸入結果中使用了函數定義時的默認值。
重新調用上面的函數
def defaultparam(name, age=23): print(‘hi,我叫:‘, name) print(‘我今年:‘, age) return defaultparam(‘徐偉‘,18)
C:\python\python.exe C:/python.py/hanshu.py
hi,我叫: 徐偉
我今年: 18
由上例函數可以看出,執行結果是我們傳入的參數。由此得知:對默認參數傳值時,函數執行時調用的是我們傳入的值。
註意:默認參數一定要放在非默認參數的後面。
設置多個默認參數
例如:
def defaultparam(name,age=23,addr=‘內蒙古‘): print(‘hi,我叫:‘, name) print(‘我今年:‘, age) print(‘我現在在:‘,addr) return print(‘--------------傳入必須參數------------------‘) defaultparam(‘杜宇恒‘) print(‘------傳入必須參數,更改第一個默認默認數值----‘) defaultparam(‘杜宇恒‘,18) print(‘------傳入必須參數,默認參數值都更改----------‘) defaultparam(‘杜宇恒‘,18,‘北京‘) print(‘----傳入必須參數,指定默認參數名並更改參數值---‘) defaultparam(‘杜宇恒‘,addr=‘北京‘) print(‘------傳入必須參數,指定參數名並更改值---------‘) defaultparam(‘杜宇恒‘,addr=‘北京‘,age=23) print(‘-----第一個默認參數不帶參數名,第二個帶--------‘) defaultparam(‘杜宇恒‘,18,addr=‘北京‘) print(‘--------------兩個默認參數都帶參數名----------‘) defaultparam(‘杜宇恒‘,age=23,addr=‘北京‘) print(‘---第一個默認參數帶參數名,第二個不帶,會報錯---‘) defaultparam(‘杜宇恒‘,age=23,‘北京‘)
C:\python\python.exe C:/python.py/hanshu.py
--------------傳入必須參數------------------
hi,我叫: 杜宇恒
我今年: 23
我現在在: 內蒙古
------傳入必須參數,更改第一個默認默認數值----
hi,我叫: 杜宇恒
我今年: 18
我現在在: 內蒙古
------傳入必須參數,默認參數值都更改----------
hi,我叫: 杜宇恒
我今年: 18
我現在在: 北京
----傳入必須參數,指定默認參數名並更改參數值---
hi,我叫: 杜宇恒
我今年: 23
我現在在: 北京
------傳入必須參數,指定參數名並更改值---------
hi,我叫: 杜宇恒
我今年: 23
我現在在: 北京
-----第一個默認參數不帶參數名,第二個帶--------
hi,我叫: 杜宇恒
我今年: 18
我現在在: 北京
--------------兩個默認參數都帶參數名----------
hi,我叫: 杜宇恒
我今年: 23
我現在在: 北京
---第一個默認參數帶參數名,第二個不帶,會報錯---
File "C:/python.py/hanshu.py", line 27
defaultparam(‘杜宇恒‘,age=23,‘北京‘)
^
SyntaxError: positional argument follows keyword argument
從以上執行結果可以總結出:
(1)無論有多少默認參數,默認參數都不能放在必須參數之前。
(2)無論有多少默認參數,若不傳入默認參數值,則使用默認值。
(3)若要更改某一個默認參數值,又不想傳入其他參數,且該默認參數的位置不是第一個,則可以通過參數名更改想要更改的默認參數值。
(4)若有一個默認參數通過傳入參數名更改參數值,則其他想要更改的默認參數都需要傳入參數名,否則會報錯。
(5)更改默認參數值時,傳入參數的順序不需要根據定義的函數中的默認參數的順序傳入,不過最好時同時傳入參數名,否則容易出現執行結果與預期不一致的情況。
可變的參數
如果需要一個函數能夠處理的參數聲明時更多,這些參數叫做可變參數。
可變參數基本語法如下:
def functionname([formal_arges,]*var_arges_tuple):
"函數_文檔字符串"
function_sutite
return[expression]
加了星號(*)的變量名會存放所有未命名的變量參數。如果變量參數在函數調用時沒有指定參數,就是一個空元組。我們也可以不向可變函數傳遞未命名的變量。
例如:
def personinfo(arg,*vartuple): print(arg) for var in vartuple: print(‘我屬於不定長參數部分:‘,var) return print(‘------------不帶可變參數-------------‘) personinfo(‘杜宇恒‘) print(‘------------帶兩可變參數-------------‘) personinfo(‘杜宇恒‘,21,‘北京‘) print(‘------------帶5個可變參數-------------‘) personinfo(‘杜宇恒‘,21,‘北京‘,123,‘內蒙古‘,‘happy‘)
C:\python\python.exe C:/python.py/hanshu.py
------------不帶可變參數-------------
杜宇恒
------------帶兩可變參數-------------
杜宇恒
我屬於不定長參數部分: 21
我屬於不定長參數部分: 北京
------------帶5個可變參數-------------
杜宇恒
我屬於不定長參數部分: 21
我屬於不定長參數部分: 北京
我屬於不定長參數部分: 123
我屬於不定長參數部分: 內蒙古
我屬於不定長參數部分: happy
可變參數的好處,我在參數前面加了一個星號,在函數內部,參數前面的星號將所有值放在同一個元組中,通過這種方式將這些值收集起來,然後使用。參數vartuple接收的是一個元組,調用函數時可以傳入任意個數的參數,也可以不傳。
還可以使用兩個"*",即使用"**"處理關鍵字的參數。
例如:
other = {‘城市‘:‘北京‘,‘愛好‘:‘pyhon‘} def persionfo(name,number,**kw): print(‘名稱:‘,name,‘學號:‘,number,‘其他:‘,kw) persionfo(‘杜宇恒‘,666,城市=other[‘城市‘],愛好=other[‘愛好‘])
C:\python\python.exe C:/python.py/hanshu.py
名稱: 杜宇恒 學號: 666 其他: {‘城市‘: ‘北京‘, ‘愛好‘: ‘pyhon‘}
函數調用時可以使用更簡單的方式調用.
例如:
other = {‘城市‘:‘北京‘,‘愛好‘:‘pyhon‘} def persionfo(name,number,**kw): print(‘名稱:‘,name,‘學號:‘,number,‘其他:‘,kw) persionfo(‘杜宇恒‘,666,**other)
C:\python\python.exe C:/python.py/hanshu.py
名稱: 杜宇恒 學號: 666 其他: {‘城市‘: ‘北京‘, ‘愛好‘: ‘pyhon‘}
執行結果和上面的一樣,寫法法上面卻簡單了不少.此處**other表示把other這個字典的所有key-value用關鍵字參數傳入到函數的**kw參數,kw將獲得一個字典,註意kw獲得的字典時other復制的,對kw的改動不會影響函數外的other.
組合參數
在python中定義函數可以使用必須參數、關鍵字參數、默認參數和可變關鍵字參數,這4種參數可以組合使用.
註意定義參數的順序必須是必須參數、默認參數、可變長參數和關鍵字參數.
def exp(p1, p2, df=0, *vart, **kw): print(‘p1=‘, p1, ‘p2=‘, p2, ‘df=‘,df, ‘vart=‘, vart, ‘kw=‘, kw) exp(1, 2) exp(1, 2, c=3) exp(1, 2, 3, ‘a‘, ‘b‘) exp(1, 2, 3, ‘abc‘, x=9)
C:\python\python.exe C:/python.py/hanshu.py
p1= 1 p2= 2 df= 0 vart= () kw= {}
p1= 1 p2= 2 df= 0 vart= () kw= {‘c‘: 3}
p1= 1 p2= 2 df= 3 vart= (‘a‘, ‘b‘) kw= {}
p1= 1 p2= 2 df= 3 vart= (‘abc‘,) kw= {‘x‘: 9}
由上例可以看到,使用了組合參數,在調用函數時,python解釋器會自動按照參數位置和參數名把隊形的參數傳進去.
還可以使用tuple和dict調用函數:
例如:
def exp(p1,p2,df=0,*vart,**kw): print(‘p1=‘,p1,‘p2=‘,p2,‘df=‘,df,‘vart=‘,vart,‘kw=‘,kw) args = (1,2,3,4) kw = {‘x‘:8,‘y‘:‘9‘} exp(*args,**kw)
C:\python\python.exe C:/python.py/hanshu.py
p1= 1 p2= 2 df= 3 vart= (4,) kw= {‘y‘: ‘9‘, ‘x‘: 8}
由上例可以看到,任意函數都可以通過類似func(*arges,**kw)的的形式調用,無論參數是如何定義的.
執行流程
(1)為了保證函數的定義先於首次調用執行,我們需要知道執行語句的順序,即執行流程.
(2)執行總是從程序的第一行代碼開始,從上到下、從左到右,按順序依次執行第一條語句.
(3)函數定義並不會改變程序的執行流程,不過函數代碼塊中的語句並不是立即執行,而是等函數被程序調用時才執行.
(4)函數調用可以看作程序執行流程中的一個迂回路徑,遇到函數調用時,並不會直接繼續執行下一條語句,而是跳到函數體的第一行,繼續執行完函數代碼塊中的所有語句,在跳回原來離開的地方.
(5)函數代碼塊中可以調用其他函數,當程序流程運行到一個函數時,可能需要執行其他函數中的語句.但當執行這個函數的語句時,又可能需要調用執行另一個函數的語句.
(6)pyhon對於程序運行到哪裏有很好的記錄,所以在每個函數結束後,程序都能跳回它離開的地方,直到執行到整個程序的結尾才會結束.
(7)python代碼不一定要一行一行按照書寫順序閱讀,有時按照執行的流程閱讀可以更好理解代碼的含義.
形參和實參
python函數的兩種類型參數,一種是函數定義裏的形參,一種時調用函數時傳入的實參.
在函數內部會將實參的賦值給形參
例如:
def personinfo(age,name): print(‘年齡:‘,age) print(‘名稱:‘,name) return
在該函數中,函數名personinfo後面的參數列表age和name就是實參,在函數體中分別將age和name的值傳遞給age和name,函數體中age和name就是形參.
註意:在函數體內部都是對形參進行操作,不能操作實參,即對實參做出更改.
內置函數組合規則在自定義函數上同樣適用.
例如:(對上例自定義的personinfo函數可以使用任何表達式作為實參)
def personinfo(age,name): print(‘年齡:‘,age) print(‘名稱:‘,name) return personinfo(21,‘杜宇恒‘*2)
C:\python\python.exe C:/python.py/hanshu.py
年齡: 21
名稱: 杜宇恒杜宇恒
由執行結果可以看到,可以使用字符串的乘法表達式作為實參.
在python中,作為實參的表達式會在函數調用前執行.在上例中,實際先執行‘杜宇恒‘*2的操作,將執行的結果作為一個實參傳遞到函數體中.
註意:作為實參傳入函數的變量名稱和函數定義裏形參的名字沒有關系.函數只關心形參的值,而不關心它在調用前叫什麽名字.
變量的作用域
簡單來說,作用域就是一個變量的命名空間.在python中,程序的變量並不是在任何位置都可以訪問,訪問權限決定於這個變量是在哪裏賦值的,代碼中變量被賦值的位置決定哪些範圍的對象可以訪問這個變量,這個範圍就是命名空間.
變量的作用域決定哪一部分程序可以訪問特定的變量名稱.python有兩種最基本的變量作用域:局部變量和全局變量.
局部變量
在函數內定義的變量名只能被函數內部引用,不能在函數外引用,這個變量的作用域是局部的,也稱為局部變量.
定義的變量如果是在函數體中第一次出現,就是局部變量.
例如:
def func(): x = 100 print(x)
在func函數中,x是在函數體中被定義的,並且也是第一次出現所以x是局部變量.
局部變量只能在函數體中被訪問,超出函數體的範圍就會報錯
例如:
def func(): x = 100 print(‘變量x:%s‘% x) print(‘函數體外訪問變量x:%s‘%x) func()
C:\python\python.exe C:/python.py/hanshu.py
Traceback (most recent call last):
File "C:/python.py/hanshu.py", line 10, in <module>
print(‘函數體外訪問變量x:%s‘%x)
NameError: name ‘x‘ is not defined
報錯:第10行的x沒有定義;原因是第10行語句沒有在函數體中.
如果把x作為實參傳入函數體中,在函數體中不定義變量x,會將x認為是怎樣的變量呢?
例如:
def func(x): print(‘局部變量x為:‘,x) func(10)
C:\python\python.exe C:/python.py/hanshu.py
局部變量x為: 10
從上例輸出結果可以看出,輸出了局部變量的值.這是應為參數的工作原理類似於局部變量,一旦進入函數體,就成為了局部變量.
如果在函數外定義了變量x並賦值,在函數體中能否使用x呢?
例如:
x = 50 def func(): print(‘x等於‘,x) func()
C:\python\python.exe C:/python.py/hanshu.py
x等於 50
由上例輸出結果可以看出,在函數體中可以直接使用函數體外的變量,這種變量稱之為全局變量.
如果在函數外定義了變量x並賦值,將x作為函數的實參,在函數體中更改x的值,函數體外x的值是否跟著變更呢?
例如:
x = 50 def func(x): print(‘x等於‘,x) x=2 print(‘局部變量x變為‘,x) func(x) print(‘x 一直是‘,x)
C:\python\python.exe C:/python.py/hanshu.py
x等於 50
局部變量x變為 2
x 一直是 50
有輸出結果可以看到,在函數體中更改變量的值並不會更改函數外的值.這是應為調用func函數時創建了新的命名空間,它作用於func函數代碼塊.賦值語句x=2只是在函數體的作用域起作用,不能影響外部作用中的x.可以看到調用x時,它的值並沒有改變.
全局變量
在函數外,一段代碼最開始賦值的變量可以被多個函數引用,這就是全局變量.全局變量可以在整個程序範圍內訪問.
例如:
total =0 #這是一個全局變量 def sum(arg1,arg2): total = arg1+arg2; #在這裏這一個局部變量 print(‘函數內部是局部變量:‘,total) return total def totalprint(): print(‘total的值是‘,total) return total print(‘函數求和結果:‘,sum(10,20)) totalprint() print(‘函數外是全局變量:‘,total)
C:\python\python.exe C:/python.py/hanshu.py
函數內部是局部變量: 30
函數求和結果: 30
total的值是 0
函數外是全局變量: 0
由上例結果可以看出,全局變量可在全局使用,在函數體中更改全局變量的值不會影響全局變量在其他函數或語句中使用.
小例子:
num = 100 def func(): num=200 print(‘函數體中的num的值為:‘,num) func() print(‘函數外的num的值為:‘,num)
C:\python\python.exe C:/python.py/hanshu.py
函數體中的num的值為: 200
函數外的num的值為: 100
我們定義了一個名為num的全局變量,在函數體中也能定義一個名為num的全局變量,在函數體中使用的時函數體中的num變量,在函數體外使用num變量時使用的是全局變量.
註意:函數中使用某個變量時,如果該變量名既有全局變量又有局部變量,就默認使用局部變量.
註意:要將全局變量變為局部變量,只需在函數體中定義一個和全局變量名稱一樣的變量即可.
局部變量變為全局變量
例如:
num = 100 print(‘函數調用前的num的值為:‘,num) def func(): global num num = 200 print(‘函數體中num的值為:‘,num) func() print(‘函數調用結束後num的值為:‘,num)
C:\python\python.exe C:/python.py/hanshu.py
函數調用前的num的值為: 100
函數體中num的值為: 200
函數調用結束後num的值為: 200
上例可以看出,在函數體中變量num前加了一個global關鍵字後,函數調用結束後,在函數外使用num變量時,值變為和函數體中的一樣的值了.
由此得知:要在函數中將某個變量定義為全局變量,在需要被定義的前面加一個關鍵字global即可.
在函數體中定義global變量後,在函數體中對變量做的其他操作也是全局性的.
例如:
num = 100 print(‘函數調用前的num的值為:‘,num) def func(): global num num = 200 num +=100 print(‘函數體中num的值為:‘,num) func() print(‘函數調用結束後num的值為:‘,num)
C:\python\python.exe C:/python.py/hanshu.py
函數調用前的num的值為: 100
函數體中num的值為: 300
函數調用結束後num的值為: 300
由上例可以看出,在函數體中對定義的全局變量num做了一次加100的操作,num的值由原來的200變為300,在函數體外獲得的num的值也變為300了.
有返回值和無返回值函數
若定義函數時沒有使用return語句,則默認返回一個None.要返回一個None,可以只寫一個return,但要返回具體的數值,就需要在return後面加上需要返回的內容.
對於函數的定義來說,使用return語句可以向外提供該函數執行的一些結果;對於函數調用者來說,是否可以使用函數中執行的一些操作結果,就在於函數是否使用return語句返回了對應的執行結果.
在python中,有的函數會產生結果(如數學函數),我們稱這種函數為返回值函數(fruitfulfunction);有的函數執行一些動作之後不返回任何值,我們稱這種函數為無返回值函數.
剛當我們調用有返回值函數時,可以使用返回的結果做相關操作;當我們使用無返回值或返回None的函數時,只能得到一個None值.
例如:
def noreturn(): print(‘noreturn函數不寫return語句‘) def justreturn(): print(‘justreturn函數只寫return,不返回具體內容‘) return def returnval(): x=10 y=20 z=x+y print(‘returnval函數寫return語句,並返回求和結果.‘) return z print(‘函數noreturn調用的結果:‘,noreturn()) print(‘函數 justreturn調用結果:‘,justreturn()) print(‘函數retrrnval調用結果:‘,returnval())
C:\python\python.exe C:/python.py/hanshu.py
noreturn函數不寫return語句
函數noreturn調用的結果: None
justreturn函數只寫return,不返回具體內容
函數 justreturn調用結果: None
returnval函數寫return語句,並返回求和結果.
函數retrrnval調用結果: 30
由上例可以看出,定義函數時不寫return或只寫一個return語句返回的都是None.如果寫了返回具體內容,調用函數時就可以獲取具體內容.
函數的好處
(1)新建一個函數,讓我們有機會為一組語句命名,成為一個代碼塊,這樣更有利於閱讀代碼塊,並且組織後的代碼更容易調試.
(2)函數方法可以減少重復代碼的使用,讓程序代碼總行數更少,之後修改代碼時只需要少量修改就可以了
(3)將一個很長的代碼片段拆分成幾個函數後,可以對每一個函數進行單獨調試,單個函數調試通過後,再將它們組合起來形成一個完整的產品.
(4)一個設計良好的函數可以在很多程序中復用,不需要重復編寫.
返回函數
函數中可以返回函數
例如:
def calc_sum(*args): ax = 0 for n in args: ax = ax + n return ax print(calc_sum(1,2,3))
C:\python\python.exe C:/python.py/hanshu.py
6
上例定義了一個可變的求和函數,該函數允許傳入多個參數,最後返回求得的和.
如果不想立刻求和,而是在後面的代碼中根據需要再計算,該怎麽辦?
例如:
def sum_late(*args): def calc_sum(): ax=0 for n in args: ax = ax + n return ax return calc_sum #對於此處定義的函數,這裏沒有返回求和的結果,而是返回了一個求個的函數 print(‘調用sum_late的結果:‘,sum_late(1,2,3,4)) calc_sum=sum_late(1,2,3,4) print(‘調用的calc_sum的結果:‘,calc_sum())
C:\python\python.exe C:/python.py/hanshu.py
調用sum_late的結果: <function sum_late.<locals>.calc_sum at 0x0000000000B4CE18>
調用的calc_sum的結果: 10
由上例結果可以看到,調用定義函數時美譽直接返回求和結果,而是返回了一串字符(這個字符其實就是函數).當執行返回函數時,才真正計算求和的結果.
註意:上例中,在函數sum_late中又定義了函數calc_sum,並且內部函數calc_sum可以引用外部函數sum_late的參數和局部變量.當sum_late返回函數calc_sum時,相關參數和變量都保存在返回的函數中,稱為閉包(Closure).這種結構威力極大.
還需要註意:當調用函數sun_late()函數時,每次調用都會返回一個新的函數,即使傳入相同參數也是如此.
閉包的定義:如果在一個內部函數裏對外部函數(不是在全局作用域)的變量進行引用,內部函數就稱作為閉包.
例如:
def count(): fs=[] for i in range(1,4): def f(): return i*i fs.append(f) return fs f1,f2,f3 = count() print(‘f1的結果是:‘,f1()) print(‘f2的結果是:‘,f2()) print(‘f3的結果是:‘,f3())
C:\python\python.exe C:/python.py/hanshu.py
f1的結果是: 9
f2的結果是: 9
f3的結果是: 9
從輸出結果可以看到三個函數返回的都是9,這是因為返回函數時引用了變量I,但它並非立刻執行.等到3個函數都返回時,它們所引用的變量i已經成了3,因此最終結果為9.
註意:返回閉包時,返回函數不要引用任何循環變量或後續會發生的變量,否則很容易出問題.
如果必須要引用循環變量怎麽辦?
如下:
def count(): def f(j): def g(): return j*j return g fs = [] for i in range(1,4): fs.append(f(i)) #f(i)立刻執行,因此i的當前值被傳入f() return fs f1,f2,f3 = count() print(‘f1的結果是:‘,f1()) print(‘f2的結果是:‘,f2()) print(‘f3的結果是:‘,f3())
C:\python\python.exe C:/python.py/hanshu.py
f1的結果是: 1
f2的結果是: 4
f3的結果是: 9
遞歸函數
如果一個函數在內部調用自身,這個函數就稱為遞歸函數.
遞歸函數的簡單定義如下:
def recurision():
return recursion()
這類遞歸函數被稱作無窮遞歸(infinite recursion),理論上是永遠不會結束.每次調用函數都會用掉一點內存,在足夠多的函數調用發生後,內存空間被占滿,程序就會報錯.
有用的遞歸函數需要滿足以下條件:
(1)當函數直接返回值時有基本實例(最小可能性問題).
(2)遞歸實例,包括一個或多個問題最小部分的遞歸調用.
使用遞歸關鍵在於將問題分解為小部分,遞歸不能永遠繼續下去,因為它總是以最小可能性問題結束,而這些問題又存儲在基本實例中.
實現函數自身調用:函數每次調用時都會創建一個命名空間,也就是當函數調用"自身"時,實際上運行的是兩個不同的函數(簡單理解為:一個函數具有兩個不同的命名空間)
遞歸示例,基數按階乘n!=1X2X3X4...Xn,用函數fact(n)表示可以看出:
fact(n)= n! =1X2X3...X(n-1)Xn=(n-1)! X n=fact(n-1)Xn
所以,fact(n)可以表示為nXfact(n-1),只有n=1時需要特殊處理.
fact(n)用遞歸方式定義函數如下:
def fatc(n): if n==1: return 1 return n * fatc(n-1) print(‘調用遞歸函數執行結果為:‘,fatc(5))
C:\python\python.exe C:/python.py/hanshu.py
調用遞歸函數執行結果為: 120
由輸出結果可以看出,函數已正確輸出5的階乘結果.
遞歸函數的優點:定義簡單,邏輯清晰.
註意:使用遞歸函數需要註意防止棧溢出.在計算機中,函數調用時通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會增加一層棧幀;每當函數返回,棧就會減一層棧幀.由於棧的大小不是無限的,因此遞歸調用的次數過多會導致棧溢出.
解決遞歸調用棧溢出的方法是通過尾遞歸優化,事實上尾遞歸和循環效果一樣,把循環看成一種特殊的尾遞歸函數也可以.
尾遞歸是指在函數返回時調用函數本身,並且return語句不能包含表達式,這樣編譯器或解釋器就可以對尾遞歸進行優化,使尾遞歸本身調用多少次都只能占用一個棧幀,從而避免棧溢出的情況.
尾遞歸優化
如下:
def fact(n): return fact_iter(n,1) def fact_iter(num,product): if num == 1: return product return fact_iter(num -1,num * product) print(‘調用遞歸函數執行結果為:‘,fact(5))
C:\python\python.exe C:/python.py/hanshu.py
調用遞歸函數執行結果為: 120
可以看到return fact_iter(num -1,num * product)僅返回遞歸函數本身,num-1和num* product 在函數調用前就會被計算,不影響函數調用.
調用尾遞歸時如果做了優化,棧就不會增長,因此無論調用多少次都不會溢出.
匿名函數
匿名函數就時不再使用def語句這樣的標準形式定義一個函數.
python使用lambda創建匿名函數.
lambda只是一個表達式,函數體比def簡單很多.
lambda的主體是一個表達式,而不是一個代碼塊,僅能在lambda表達式中封裝有限的邏輯.lambda函數擁有自己的命名空間,不能訪問自有參數列表之外或全局命名空間的參數.
lambda函數的語法只包含一個語句,如下:
lambda[arg1[,arg2,.....argn]]:exprssion
案例比較:
例如:求一個列表中大於3的元素.
方法一:通過程式編程實現,也是常規的方法,如下:
L1=[1,2,3,4,5] L2=[] for i in L1: if i >3: L2.append(i) print(‘列表中大於3的元素有:‘,L2)
C:\python\python.exe C:/python.py/hanshu.py
列表中大於3的元素有: [4, 5]
方法二:通過函數式編程實現,運用一個filter,給出一個判斷條件,如下:
def func(x): return x>3 f_list=filter(func,[1,2,3,4,5]) print(‘列表中大於3的元素有:‘,[item for item in f_list])
C:\python\python.exe C:/python.py/hanshu.py
列表中大於3的元素有: [4, 5]
方法三:使用匿名函數,如下:
print(‘列表中大於3的元素有:‘,[item for item in filter(lambda x:x>3,[1,2,3,4,5])])
C:\python\python.exe C:/python.py/hanshu.py
列表中大於3的元素有: [4, 5]
從方法三可以看出,lambda一般應用於函數式編程,代碼簡潔,常和filter等函數結合使用.
對方法三使用lambda的示例進行解析:
x 為 lambda 函數的一個參數.
: 為分割符
x>3 則是返回值,在lambda函數中不能有retrrn,其冒號(:)後面就是返回值.
item for item in filter 是python3中filter函數的取值方式,應為從python3起,filter函數返回的對象從列表改為叠代器(filter object).filter object支持叠代操作,比如for循環:
匿名函數的應用場景:
(1)如果程序一次性使用\不需要定義函數名時,用匿名函數可以節省內存中的變量定義空間.
(2)如果想讓程序更加簡潔,使用匿名函數就可以做到
匿名函數的三個規則
(1)一般有一行表達式,必須有返回值
(2)不能有return.
(3)可以沒有參數,也可以有一個或多個參數.
示例:
無參匿名函數:
t = lambda : True print(t())
C:\python\python.exe C:/python.py/hanshu.py
True
帶參數匿名函數:
lambda x: x**3 # 一個參數 lambda x,y,z:x+y+z #多個參數 lambda x,y=3:x*y#允許參數存在默認值
匿名函數調用:
c = lambda x,y,z:x*y*z print(c(2,3,4)) c=lambda x,y=2:x+y#使用了默認值 print(c(10))#如果不輸入,就使用默認值2
C:\python\python.exe C:/python.py/hanshu.py
24
12
偏函數
偏函數是將索要承載的函數作為partial()函數的第一個參數,原函數的各個參數依次作為partial()函數的後續參數,除非使用關鍵字參數.
示例:實現一個取余函數,取得整數100對不同數m的100%m的余數,如下:
from functools import partial def mod(n,m): return n%m mod_by_100 = partial(mod,100) print(‘自定義函數,100對7取余結果為:‘,mod(100,7)) print(‘調用偏函數,100對7取余結果為:‘,mod_by_100(7))
C:\python\python.exe C:/python.py/hanshu.py
自定義函數,100對7取余結果為: 2
調用偏函數,100對7取余結果為: 2
偏函數可以降低函數函數調用的難度
本文出自 “duyuheng” 博客,謝絕轉載!
python3.5修煉手冊12