一. 一切皆物件
函式式編程并沒有標準定義,如果代碼非常繁瑣則考慮使用,
學習閉包的概念,不是python獨有的,
其他大多數語言中的函式只是一段可執行的代碼,并不是物件,
python中的函式是物件,一切皆物件,可以把函式賦值給變數:
a = 1
a = '2'
a = def
甚至可以把函式當作另外一個函式的引數傳遞或者當成回傳值回傳,而C#中要封裝成委托,
二.什么是閉包:閉包=函式+函式定義時的環境變數
我們嘗試從概念上去理解一下閉包,
在一些語言中,在函式中可以(嵌套)定義另一個函式時,如果內部的函式參考了外部的函式的變數,則可能產生閉包,閉包可以用來在一個函式與一組“私有”變數之間創建關聯關系,在給定函式被多次呼叫的程序中,這些私有變數能夠保持其持久性,—— 維基百科
用比較容易懂的人話說,就是當某個函式被當成物件回傳時,夾帶了外部變數,就形成了一個閉包,
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的值
五.小談函式式編程
- python和Javascript都會使用閉包,
- 閉包特點:在一個函式外部間接呼叫函式內部的變數,從模塊級別間接呼叫區域變數,
- 極易造成記憶體泄漏,
閉包的概念
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/197783.html
標籤:Python
上一篇:Python3(八) 列舉詳解
