在一些語言中,在函式中可以(巢狀)定義另乙個函式時,如果內部的函式引用了外部的函式的變數,則可能產生閉包。閉包可以用來在乙個函式與一組「私有」變數之間建立關聯關係。在給定函式被多次呼叫的過程中,這些私有變數能夠保持其永續性。
1.在乙個外函式中定義了乙個內函式,內函式裡運用了外函式的臨時變數,並且外函式的返回值是內函式的引用。這樣就構成了乙個閉包。
一般情況下,在我們認知當中,如果乙個函式結束,函式的內部所有東西都會釋放掉,還給記憶體,區域性變數都會消失。但是閉包是一種特殊情況,如果外函式在結束的時候發現有自己的臨時變數將來會在內部函式中用到,就把這個臨時變數繫結給了內部函式,然後自己再結束。
2.望文知意,可以形象的把它理解為乙個封閉的包裹,這個包裹就是乙個函式,包裹裡面的東西就是自由變數,自由變數可以在隨著包裹到處遊蕩。乙個閉包就是你呼叫了乙個函式a,這個函式a返回了乙個函式b給你,而且函式b引用了乙個a中的變數。這個返回的函式b就叫做閉包。你在呼叫函式a的時候傳遞的引數就是自由變數。
根據以上,其實我們自己就可以總結出在python語言中形成閉包的三個條件,缺一不可:
1.必須有乙個內嵌函式(函式裡定義的函式)——這對應函式之間的巢狀
2.內嵌函式必須引用乙個定義在閉合範圍內(外部函式裡)的變數——內部函式引用外部變數
3.外部函式必須返回內嵌函式——必須返回那個內部函式
建立乙個最簡單的閉包
```
def outfun(): #外部函式
x=100 #自由變數
def innerfun(y): #內部函式
nonlocal x #非區域性變數
x+=y
print(x)
return innerfun #返回內部函式引用
t1=outfun()
t1(1)
```
####1. 外函式返回內部函式的引用 ####
t1=outfun()將outfun()函式的返回值(即innerfun的引用)賦值給t1,那麼t1就是內部函式innerfun()的引用,那麼什麼是引用呢?在python中一切都是物件,比如a=1,這其實是在記憶體中開闢了一塊空間來儲存數字1,而a
這個變數名就儲存了1所在記憶體的位置引用。這裡的引用就像是c語言裡面的指標,大家可以把引用理解成儲存位址,a只是乙個變數名,a裡面儲存的就是1這個數值所在的記憶體位置,就是a裡面儲存了1的引用。同樣的函式的引用亦是如此!函式定義的時候就是在記憶體裡面開闢一片空間來儲存函式內部**以及內部變數等。innerfun只不過是乙個變數名字,而其裡面卻儲存了對innerfun()這個函式所在位置的引用,你也可以這樣操作b=innerfun,那麼就把函式的所在位置也賦值給了b,那麼b()和innerfun()是相等身份的。
2.外部函式把臨時變數繫結給內函式
在正常情況下,當乙個函式執行結束後,會把自己的臨時變數釋放給記憶體,之後這些臨時變數都不會存在了。但是在閉包中卻不是這樣的,外部函式發現自己的臨時變數會在將來的內部函式中使用,於是在自己結束生命週期的時候,返回內部函式的同時,會把在內部函式中用到的臨時變數繫結到內部函式中,因此即使外部函式結束了,它內部的臨時變數還是依然可以在內部函式中使用的,這就是原因所在。這一點需要我們好好理解到位!
內部函式修改外部函式的臨時變數
我們可以將外部函式的臨時變數繫結到內部函式中,保證了在外部函式結束生命週期的時候不會讓臨時變數釋放掉,那麼怎麼在內部函式中修改繫結了的外部函式的臨時變數呢?
在python基礎語法中,我們為了在函式內部修改乙個全域性變數,一般有兩種方法:1.在函式內部使用global宣告變數為全域性變數,即可修改。2.將全域性變數的型別轉換成可變型別,如:列表,就可以實現在函式內部進行修改
那麼在閉包裡面其實也有兩種方法可以滿足我們的要求:1.使用nonlocal宣告臨時變數,將其變為全域性變數。2.將外部函式的臨時變數定義為可變資料型別的變數,這樣就可以直接在內部函式中修改了。我們在上面的那個程式就是演示了方法1的使用。
```
def outfun(): #外部函式
x=100 #自由變數
def innerfun(y): #內部函式
nonlocal x #非區域性變數
x+=y
print(x)
return innerfun #返回內部函式引用
t1=outfun()
t1("""
閉包的作用
以下介紹一下閉包的主要作用,其他的作用請在開發過程中多多體會!
作用:
閉包可以讓臨時變數在外部函式生命週期結束時,仍然存在於記憶體之中!
例1
比如說,如果你希望函式的每次執行結果,都是基於這個函式上次的執行結果。我以乙個類似棋盤遊戲的例子來說明。假設棋盤大小為50*50,左上角為座標系原點(0,0),我需要乙個函式,接收2個引數,分別為方向(direction),步長(step),該函式控制棋子的運動。棋子運動的新的座標除了依賴於方向和步長以外,當然還要根據原來所處的座標點,用閉包就可以保持住這個棋子原來所處的座標。
```
origin = [0, 0]
legal_x = [0, 50]
legal_y = [0, 50]
def create(pos=origin):
def player(direction,step):
new_x = pos[0] + direction[0]*step
new_y = pos[1] + direction[1]*step
pos[0] = new_x
pos[1] = new_y
#注意!此處不能寫成 pos = [new_x, new_y],因為引數變數不能被修改,而pos是容器類的解決方法
return pos
return player
player = create() # 建立棋子player,起點為原點
print player([1,0],10) # 向x軸正方向移動10步
print player([0,1],20) # 向y軸正方向移動20步
print player([-1,0],10) # 向x軸負方向移動10步
```
```
[10, 0]
[10, 20]
[0, 20]
``
觀察結果可以發現,每一次移動棋子,都是在上一次移動後為基礎來移動的,除非重啟程式後,pos才會為[0,0],此時才是真正的原點位置。
例2
我們首先來建立乙個閉包:
```
def funx():
x=5def funy():
nonlocal x
x+=1
return x
return funy
```
那麼在互動環境下執行驗證
```
>>> a=funx()
>>> a()
6>>> a()
7>>> a()
8>>> a()
9```
```
>>> a.__closure__
(,)>>> type(a.__closure__)
>>> type(a.__closure__[0])
>>> a.__closure__[0].cell_contents
9>>> a()
10>>> a.__closure__[0].cell_contents
10>>> def test():pass
>>> test.__closure__==none
true
>>>
```
這樣我們就明白了,形成閉包之後,閉包函式會獲得乙個非空的__closure__屬性(對比我們最後的函式test,test是乙個不具備閉包的函式,它的__closure__屬性是none),這個屬性是乙個元組。元組裡面的物件為cell物件,而訪問cell物件的cell_contents屬性則可以得到閉包變數的當前值(即上一次呼叫之後的值)。而隨著閉包的繼續呼叫,變數會再次更新。所以可見,一旦形成閉包之後,python確實會將__closure__和閉包函式繫結作為儲存閉包變數的場所。 閉包的概念
因此 可以訪問外部函式的變數,其內部變數只能內部可訪問 閉包時塊級作用域,可以定義自己的變數,避免變數命名衝突,汙染外部變數 使用場景一 封裝私有變數,對外暴露get,set方法或其中一種 使用場景二 儲存外部函式的變數 使用場景三 使用場景三 當閉包被賦值給乙個生命較長的變數時,其所依賴的父函式的...
閉包的概念 轉
首先,我覺得,乙個概念,如果不理解也不影響使用的話,那麼,就沒必要去理解它 去學習它。閉包就是這樣乙個概念,你不理解它也能很好的用它。俺這兩年寫as3程式,是天天在和它打交道,甚至有過乙個function套乙個,乙個方法中套了20多個function的極端例子,但從未深究過它是怎麼實現的,它就像水和...
程式設計的邏輯思維
作為乙個剛從大學畢業的學生,還沒有真正的接觸到專案,對於設計的過程還有太多的不懂,懂的地方也都是一些皮毛,今天在csdn上看到那麼快的就能解決問題,那麼高的積分,感覺到自己什麼也不懂,現在的工作還是在日本,日語還比是那麼的好,所以工作起來就有點累,經常用到的就是csdn,從中學到不少的東西,要好好地...