函數式編程並無標準定義,若是代碼很是繁瑣則考慮使用。python
學習閉包的概念,不是python獨有的。web
其餘大多數語言中的函數只是一段可執行的代碼,並非對象。編程
python中的函數是對象,一切皆對象。能夠把函數賦值給變量:閉包
a = 1架構
a = '2'app
a = def函數式編程
甚至能夠把函數看成另一個函數的參數傳遞或者當成返回值返回,而C#中要封裝成委託。函數
咱們嘗試從概念上去理解一下閉包。學習
在一些語言中,在函數中能夠(嵌套)定義另外一個函數時,若是內部的函數引用了外部的函數的變量,則可能產生閉包。閉包能夠用來在一個函數與一組「私有」變量之間建立關聯關係。在給定函數被屢次調用的過程當中,這些私有變量可以保持其持久性。—— 維基百科spa
用比較容易懂的人話說,就是當某個函數被當成對象返回時,夾帶了外部變量,就造成了一個閉包。
1.
code1
def curve_pre(): def curve(): pass curve() #找不到,由於curve()的做用域僅限於curve_pre()的內部
code2
def curve_pre(): def curve(): print('This is a function') pass return curve #函數能夠做爲結果返回 f = curve_pre() #函數能夠賦值 f()#執行函數,不會報錯 #This is a function
code3
def curve_pre(): a = 25 #局部變量在curve的外部 def curve(x): #接受拋物線的x值 return a * x * x return curve #返回一個函數 f = curve_pre() print(f(2)) #調用curve()函數 #100
code4
a = 10 def f1(x): return a * x * x print(f1(2)) #40 #局部變量找不到會去上一級找
code 3 和 code4 對比
def curve_pre(): a = 25 #局部變量在curve的外部 def curve(x): #接受拋物線的x值 return a * x * x return curve #返回一個函數 a = 10 #定義a = 10 f = curve_pre() print(f(2)) #調用curve()函數 #100 #仍然是a = 25的取值,取得是定義時的環境變量,這就是閉包
函數及其外部環境變量所構成的總體叫作閉包
2.環境變量要在函數外部,但不能是全局變量:
a = 25 #a定義爲了全局變量 def curve_pre(): def curve(x): #接受拋物線的x值 return a * x * x return curve #返回一個函數 a = 10 f = curve_pre() print(f(2)) #調用curve()函數 #40 #a的值被改變了,由於沒有造成閉包
查看:
def curve_pre(): a = 25 #局部變量在curve的外部 def curve(x): #接受拋物線的x值 return a * x * x return curve #返回一個函數 a = 10 f = curve_pre() print(f.__closure__) print(f.__closure__[0].cell_contents)#取出閉包的環境變量 print(f(2)) #調用curve()函數 #(<cell at 0x0031AAF0: int object at 0x0FF93A80>,) #25 #這裏是a的值 #100
1.閉包的意義:閉包保存的是一個環境,把函數現場保存起來了。
閉包 = 函數 + 函數定義時的環境變量(不能是全局變量)
2. 閉包的經典誤區 從外層向內層分析
def f1(): a = 10 def f2(): a = 20 #對a從新賦值局部變量,不會影響到外部 print(a) print(a) #10 f2() #20 #f1內部調用f2 print(a) #10 f1() #10 #20 #10
驗證其是否是一個閉包:
1
def f1(): a = 10 def f2(): a = 20 # print(a) #print(a) f2() #print(a) f = f1() #f1是None類型 print(f.__closure__) #報錯
(2)加上返回值,仍然不是閉包函數:
def f1(): a = 10 def f2(): a = 20 #a被認爲是一個局部變量了,就不認爲是個環境變量了 return a return f2 f = f1() print(f.__closure__) #沒有__closure__屬性 #None
(3)去掉f2()中的賦值後是閉包函數:
def f1(): a = 10 def f2(): #a = 20 #刪除a = 20,此時a被python認爲是一個局部變量 return a return f2 f = f1() print(f.__closure__) #(<cell at 0x02F5AAF0: int object at 0x0FF93990>,)
緣由:環境變量不能看成一個變量去賦值,而是必定要去引用外部。
閉包不是必不可少的東西,只是能夠使你的代碼架構更加合理。
題目:
旅行者,x = 0 爲起點,沒走一步加1,計算出旅行者當前所處的位置。
關鍵點:每次調用時須要調用上次的結果
1.先用非閉包解決一下
origin = 0 def go(step): global origin #將origin變成全局變量 new_pos = origin + step origin = new_pos #等號左邊的origin被python認識是局部變量 return origin print(go(2)) print(go(3)) print(go(6)) #2 #5 #11
2.再用閉包解決一下
origin = 0 def factory(pos): #工廠模式 def go(step): nonlocal pos #強制聲明不是局部變量 new_pos = pos + step pos = new_pos return new_pos return go tourist = factory(origin) print(tourist(2)) print(origin) print(tourist(3)) print(tourist(6)) #2 #0 #5 #11
並無改變全局變量origin的值