Python第三章__函數式編程、遞歸、閉包 歡迎加入Linux_Python學習群 群號:478616847 目錄: 函數式編程 傳參與返回值 遞歸 匿名函數 閉包 高階函數 內置函數 函數式編程 傳參與返回值 遞歸 匿名函數 高階函數 內置函數 在第三章,我們引入新的概念函數,在以往的代碼編寫中 ...
Python第三章__函數式編程、遞歸、閉包
歡迎加入Linux_Python學習群
群號:478616847
目錄:
-
函數式編程
-
傳參與返回值
-
遞歸
-
匿名函數
- 閉包
-
高階函數
-
內置函數
在第三章,我們引入新的概念函數,在以往的代碼編寫中我們都是用的過程式編程,函數式編程的特點將過程式編程變成易於管理調用的小模塊,
讓重覆的代碼可以反覆的調用,大大減少代碼量,懶惰即美德
一、函數式編程
創建函數
一個函數式由關鍵字 def ,與函數名與括弧冒號,和括弧中的參數組成,當想要執行函數的時候只需要寫上函數名加括弧即可
格式: def function (parameter) 下麵就創建一個函數
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 #創建函數 5 def print_str(): 6 print("Hello World") 7 8 #調用函數 9 print_str()第一個函數
變數的作用域
我們看一個例子,在下麵代碼中我們先把變數 a=“haha” 然後在函數中把 a=“heihei” 最後執行這個函數,並輸出變數a的結果
我們發現為什麼a不等於 "heihei",而是我們之前賦值的 “haha” 這個現象我們馬上揭秘
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 5 a = "haha" 6 7 #創建函數 8 def print_str(): 9 a = "heihei" 10 print("Hello World") 11 12 #調用函數 13 print_str() 14 print("我是變數a:",a)變數的作用域例一
全局變數與局部變數
很明顯變數的作用域就是變數在哪一個範圍內生效,如果出了這個範圍則無法使用
全局變數:通常全局變數存活在腳本運行結束,並且所有的函數都可以訪問它
局部變數:只能局部變數所在的函數內部調用,並且除非把局部變數聲明成全局變數否則,其他函數均無法調用,並且局部變數
當所在的函數執行完成後就不存在了
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 5 a = "haha" 6 7 #創建函數 8 def print_str(): 9 a = "heihei" 10 print("我是局部變數a:",a) 11 12 13 #調用函數 14 print_str() 15 print("我是全局變數a:",a)全局變數與局部變數
global
global就是可以把局部變數變成全局變數的,如果被聲明的局部變數名與全局變數名一樣的話,那麼局部變數會覆蓋全局變數,切
使用global聲明變數需要在變數之前聲明否則python會告訴你,你需要在 a 前面聲明
報錯提示:SyntaxWarning: name 'a' is assigned to before global declaration global a
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 a = "haha" 5 6 #創建函數 7 def print_str(): 8 global a 9 a = "heihei" 10 print("我是局部變數a:",a) 11 12 #調用函數 13 print_str() 14 print("我是全局變數a:",a)gloable
二、傳參與返回值
傳參
函數用起來比較簡單也容易理解,但是參數的變化就比較多了,在函數括弧內就是參數,參數可以接收字元串,數字,也可以接收字典和列表
並且在調用的時候,我們還可以指定給哪個參數賦什麼值
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 #創建函數 5 def print_str(Name,Age,Aender): 6 print(''' 7 Name:%s 8 Age:%s 9 Aender:%s 10 '''%(Name,Age,Aender)) 11 12 #用戶輸入 13 in_name = input("請輸入你的名字:") 14 in_age = input("請輸入你的年齡:") 15 in_aender = input("請輸入你的性別:") 16 17 #固定位置傳參 18 print_str(in_name,in_age,in_aender) 19 print("----------------------------------") 20 21 #不固定位置傳參 22 print_str(in_name,Aender=in_aender,Age=in_age)傳參
當我們想要傳入列表或者字典時需要怎麼辦?
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def print_str(lists): 5 if type(lists) == list: 6 print(''' 7 Name:%s 8 Age:%s 9 Aender:%s 10 '''%(lists[0],lists[1],lists[2])) 11 else: 12 print(''' 13 Name:%s 14 Age:%s 15 Aender:%s 16 '''%(lists["name"],lists["age"],lists["aenber"])) 17 18 #傳入列表 19 userlist = ["Ben","22","Man"] 20 print_str(userlist) 21 22 print("----------------------------------") 23 #傳入字典 24 userdict = {"name":"Ben","age":"022","aender":"Man"} 25 print_str(userlist)傳入字典或列表
預設參數
在函數中還可以設置預設參數,預設參數的意思是這個參數你可以傳值也可以不傳值,當不傳值的時候這個參數就等於預設值
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def print_str(lists,Country="China"): 5 if type(lists) == list: 6 print(''' 7 Name:%s 8 Age:%s 9 Aender:%s 10 country:%s 11 '''%(lists[0],lists[1],lists[2],Country)) 12 else: 13 print(''' 14 Name:%s 15 Age:%s 16 Aender:%s 17 country:%s 18 '''%(lists["name"],lists["age"],lists["aenber"],Country)) 19 20 #傳入列表 21 userlist = ["Ben","22","Man"] 22 print_str(userlist) 23 24 print("----------------------------------") 25 #傳入字典 26 userdict = {"name":"Ben","age":"022","aender":"Man"} 27 print_str(userlist,"America")預設參數
非固定參數
非固定參數的意義在於可以接收任意個值,在你的函數不確定用戶想傳入多少個值的時候使用,當然在調用有隻有非固定參數的函數的時候我們可以不傳參數。
非固定參數分一個*和兩個*,*args會把傳入的參數變成元祖,**kwargs把傳入的參數變成字典,當然*ages可以是別的名稱,但是在規範中最好使用*args,和**kwargs
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def print_str(*args,**kwargs): 5 print("我是*",args) 6 print("我是**",kwargs) 7 8 #傳入列表 9 userlist = ["Ben","22","Man"] 10 print_str(userlist,"America") 11 print("----------------------------------") 12 #傳入字典 13 print_str(A = "1",B = "2")非固定傳參
既然形參可以帶*和**,那麼實參也是可以帶*和**,那麼*就是配合列表使用的,**就是配合字典的!
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def print_str(*args,**kwargs): 5 print("我是*",args) 6 print("我是**",kwargs) 7 8 #傳入列表 9 userlist = ["Ben","22","Man"] 10 print_str(*userlist) 11 12 print("----------------分隔符----------------") 13 14 #傳入字典 15 userdict = {"name":"Ben","age":"022","gender":"Man"} 16 print_str(**userdict) 17 18 解參解參
實參帶*就會把列表分解成 "Ben","22","Man" 一個一個單獨的元素傳入函數,而**會把字典分解成name="Ben",age="022",gender="Man"
這種鍵值對傳入函數。
形參與實參
形參:
變數只有在被調用時才分配記憶體單元,在調用結束時,即刻釋放所分配的記憶體單元。因此,形參只在函數內部有效。函數調用結束返回主調用
函數後則不能再使用該形參變數
實參:
可以是常量、變數、表達式、函數等,無論實參是何種類型的量,在進行函數調用時,它們都必須有確定的值,以便把這些值傳送給形參。因此
應預先用賦值,輸入等辦法使參數獲得確定值
註!當普通參數和預設參數和非固定參數結合使用的時候,要遵循一個順序,普通參數在預設參數前面,預設參數在非固定參數前面
返回值
在正常使用函數的時候,函數是可以把函數內部處理的結果返回給函數調用者的,在沒有返回值得函數中會保留None傳給函數調用者,返回值可以返回序列等
在函數執行的時候遇到return函數會停止執行,並返回結果
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 #創建函數 5 def print_str(Age): 6 if int(Age) <= 30: 7 return "你才%s啊!真年輕"%(Age) 8 else: 9 return "你都%s啦!老家伙"%(Age) 10 11 in_age = input("請輸入你的年齡:") 12 word = print_str(in_age) 13 print(word)return
嵌套函數
在函數內部也可以寫函數,這樣就是外層函數套著內側函數,這種形式稱之為嵌套函數,同理因為作用域的關係嵌套函數只能內部調用
return unction_2(stra)+"我是第二層\n" 就等於先 c = unction_2(stra) 然後 return c+"我是第二層\n"
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def unction(stra): 2 def unction_2(stra_2): 3 return stra_2+"我是第三層\n" 4 return unction_2(stra)+"我是第二層\n" 5 6 r_str = unction("") 7 print(r_str+"我是第一層")嵌套函數
三、遞歸
前面已經介紹了函數相關的知識,在函數中,函數可以調用其他的函數,並且函數還可以調用自身,利用這種特性我們可以完成一些特定的
操作,這種函數調用自身的形式就是遞歸
def recursion() :
return recursion()
在遞歸中不能像上面兩行一樣一直調用自身,這樣一會程式就會崩潰,因為它永遠的在調用就跟while死迴圈一樣出不去,所以遞歸也需要進
判斷給它出口
例子:階乘
什麼是階乘,階乘就是給一個自然數N,然後計算N的階乘那麼 N = 1x2x3x4....N ,這個就是階乘,我們可以把它到過來看,
N = N x (n-1) x (n-2) x (n-3) ...... 1 一直乘到括弧中的值等於1,既然知道了階乘是什麼,那麼我們來寫一個程式實現它
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def factorial(n): 2 for i in range(1,n): 3 n *= i 4 return n 5 c = factorial(4) 6 print(c)階乘非遞歸版本
剖析:上面的例子首先把n=4傳入進去,然後通過 for i in range(1,4)讓i分別等於1,2,3,然後進行 n*=i,我們可以看出這個for迴圈是迴圈3次的
第一次(n = n*i) n = 4*1 ,此時n還是等於4
第二次(n = 4*i) n = 4*2 此時n = 8
第三次(n = 8*i) n = 8*3 此時n等於24
此時for迴圈了3次所以結束了,通過return把n的結果返回,所以最終結果算出 4的階乘等於24
遞歸版本
下麵看遞歸版本的階乘
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def factorial(n) : 2 if n == 1: 3 return 1 4 else: 5 return n * factorial(n-1) 6 c = factorial(4) 7 print(c)階乘遞歸版本
剖析:
首先c = factorial(4)開始執行函數,然後進行第一次判斷 n == 1,顯然第一層n不等於1,然後碰到return n * factorial(n-1),碰到return本來是要返回的,但是 factorial(n-1)
有調用了factiorial這個函數,因此進入了第二層
第二層因為上一層傳入的參數是n-1,所以第二層的n是等於3的,然後判斷,這一層的n也不等於1,然後又進入第三層
第三層n等於3,然後判斷這一層的n還不等於1,然後又進入第四層
到第四層的時候這時的 n就等於1,所以觸發了 return 1 不再調用函數了,所以就開始返回
返回第三層 return n * factorial(n-1) , 此時factorial(n-1) 就等於第四層return上去的1,所以第三層返回時就等於return n * 1(return 2 * 1),並且第三層n是等於2的
返回第二層factorial(n-1)就等於第三層return上去的2,並且第二層n是等於3的,return 3 * 2
返回第一層factorial(n-1)就等於第二層return上去的6,並且第一層n是等於4的,return 4 * 6
到此為止遞歸執行完畢,c就等於 4 * 6 c=24
四、匿名函數
匿名函數也叫lambda函數,函數沒有具體的名稱。語法:function name= lambda args(多個參數用逗號隔開): Expression(表達式,表達式的結果就是返回值)
先來看一個最簡單例子:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #普通函數 2 def func(arg1,arg2): 3 return arg1-arg2 4 5 #lambda函數 6 func_2 = lambda arg1,arg2: arg1-arg2 7 8 #傳參執行 9 print(func(5,4)) 10 print(func_2(5,4))匿名函數
有認識,這個匿名函數和普通函數沒有什麼區別麽,其實匿名函數就像三元運算一樣,並且能夠用lambda函數有幾大優勢
1、在一些不會再別的地方調用的函數,我們可以使用匿名函數,並且這樣簡化了代碼,看起來更加整潔。
2、lambda函數將會搭配一些內置函數來使用(下麵會涉及到)
五、閉包
在上面的示例中知道了函數可以調用函數本身,這種形式稱之為遞歸,那麼還可以將函數作為參數返回,這種形式就稱之為閉包
閉包最大的好處就是即用即調,閉包對於安裝計算,隱藏狀態,以及在函數對象和作用域中隨意地切換是很有用的!
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 def func(rate): 4 count = [0] 5 def add_func(arg): 6 count[0] +=1 7 print("第%s次調用"%count[0]) 8 arg = arg - arg*rate 9 return arg 10 return add_func 11 12 closure = func(0.03) 13 print(closure(1000)) 14 print(closure(1100)) 15 print(closure(1200))閉包示例
例子中做了一個減去手續費後返回餘額的功能,首先執行了func函數,將利率封裝了進去,然後,func函數把它內部的函數進行了進行了返回
要知道當函數不加括弧的時候是不執行的!,所以此時closoure就是 add_func 函數的記憶體地址,當想要使用這個功能的時候,直接把closoure加括弧
並傳入值即可執行。並且可以看到的是在全局作用域中執行,隨時可以切換到局部作用域。
六、高階函數
函數可以用來當做返回值,可以用調用自己本身,高階函數就是函數的參數把另一個函數作為參數,這種函數就稱之為高階函數。
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def func_1(num): 2 return num+1 3 4 def func_2(num): 5 return num-1 6 7 def func_main(num,func): 8 # 可以簡寫成return func(num) 9 results = func(num) 10 return results 11 12 results = func_main(10,func_1) 13 print(results) 14 print(func_main(10,func_2))高階函數
編寫高階函數,就是讓函數的參數能夠接收別的函數。
七、內置函數
內置函數就是python中內置的一些方法,內置函數官方介紹請猛戳這裡
內置函數使用方法示例,詳細介紹請參考
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # !/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 #返回數字的絕對值。 參數可以是整數或浮點數。 如果參數是複數,則返回其大小。 5 print(abs(-1.11)) 6 7 #傳入一個可被迴圈的元素,如果這個元素中有一個為False則都為假 8 # 0 空值 False 都為假 9 print(all([1,2,3])) 10 11 #與all相反,只有一個為真,則為真; 12 print(any([0,2,False])) 13 14 #這個函數跟repr()函數一樣,返回一個可列印的對象字元串方式表示。當遇到非ASCII碼時 15 #就會輸出\x,\u或\U等字元來表示。與Python 2版本里的repr()是等效的函數。 16 print(ascii("dsads"),ascii(66),ascii('b\23')) 17 18 #將十進位轉換為二進位; 19 print(bin(10)) 20 21 #返回布爾值,即True或False之一,如果參數為false或省略,則返回False; 否則返回True。 22 print(bool(1)) 23 24 #根據傳入的參數創建一個新的位元組數組 25 #如果傳入字元串必須給出編碼 26 print(bytearray('你好','utf-8')) 27 #當source參數是一個可迭代對象,那麼這個對象中的元素必須符合大於0 小於256 28 print(bytearray([256,1,2])) 29 30 #返回一個的“bytes”對象,返回bytes類型 31 bytes('中文','utf-8') 32 33 #檢查對象是否可以被調用 34 def func(): 35 pass 36 print(callable(func)) 37 38 #返回整數所對應的Unicode字元,chr(97)返回字元串'a',而chr(8364)返回字元串'€'。 39 print(chr(126)) 40 41 #是用來指定一個類的方法為類方法,類方法可以不實例化直接調用 42 class A: 43 @classmethod 44 def B(cls,arg1,): 45 print(arg1) 46 A.B(1) 47 A().B(1) 48 49 #將源編譯為代碼或者AST對象。代碼對象能夠通過exec語句來執行或者eval()進行求值。 50 #源可以是正常字元串,位元組字元串或AST對象。 51 expr = "5+5-1" 52 obj = compile(expr,"","eval") 53 print(eval(obj)) 54 55 #返回值為real + imag * j的複數或者轉化一個字元串或數為複數。如果第一個參數為字元串,則不需要指定第二個參數。 56 print(complex(1, 2)) 57 print(complex(1)) 58 print(complex("1+2j")) 59 60 61 62 # 參數是一個對象和一個字元串。 該字元串必須是對象屬性之一的名稱。 63 class A: 64 def a1(self): 65 print("a1") 66 def a2(self): 67