標籤:閉包 python閉包 python
Python閉包的學習
什麼是閉包?
借用維基上解釋:在電腦科學中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變數的函數。這個被引用的自由變數將和這個函數一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體。閉包在運行時可以有多個執行個體,不同的引用環境和相同的函數組合可以產生不同的執行個體。
好吧,看了這段定義的確不是還不能立即理解閉包到底是什麼。不過,閉包並不是很難理解,往下看幾個小例子就能明確它到底是什麼了。
前提介紹:
在瞭解閉包前,我們需要瞭解python文法中變數的作用範圍(對理解後面的閉包的例子有協助)。Python從文法上支援多個函數嵌套。在python現有的(2.1版本後)嵌套規則下,嵌套函數可以訪問全域範圍自己本身的範圍以及自己之上的函數的局部範圍。如(來自python核心編程一書):
變數範圍例一:
x = 1def foo():y = 2def bar():z = 3print x + y + zbar()foo()
結果為輸出:
6
解釋:
也就是bar()函數能夠訪問全域變數x,自己的局部範圍z,並且還能訪問嵌套自己的foo()函數的局部變數y。
接著,我們還需要知道在python2.x中,裡層函數雖然能夠訪問外層函數的變數,但是卻不能對其賦值。我們對例一稍作修改:
變數範圍例二:
x = 1def foo():y = 2def bar():z = 3y += 1print x + y + zbar()foo()
結果:
此時運行將報錯:UnboundLocalError: local variable ‘y‘ referenced before assignment。
這個錯誤的意思是說本地變數y在引用前沒有被定義。這是因為在裡層函數裡對變數進行賦值時,python將認為這個變數是本地的,所以如果有修改變數的值就會變成局部變數(當然如果沒有修改,那麼python將先在自己定義的範圍內尋找,如果沒有則去外層尋找,這樣就知道了y是個外層的變數)。
這在python 3.x中可以使用nonlocal來解決,nonlocal關鍵字是用來在函數或其他範圍中使用外層(非全域)變數的。
瞭解了python中的變數範圍後,我們來看幾個閉包的例子(來自python核心編程一書):
閉包例一:
def counter(start_at=0):count = [start_at]def incr():count[0] += 1 return count[0]return incr
結果:
>>> count = counter(5)>>> print count()6>>> print count()7>>> count2 = counter(100)>>> print count2()101
解釋:
在counter函數中,我們定義了一個incr函數,incr函數的作用就是將count[0]的值加一,counter函數返回incr函數。注意,要知道,python中一切階對象,函數也是可以作為函數對象進行傳遞和返回的。
當我們執行count = counter(5)時,count就被賦予了incr這個函數對象,也就說可以調用count就相當於調用incr函數。另外,我們給counter函數一個參數5,那麼count[0]的初始值就為5了,把這個返回來的給定了參數的函數對象賦值給了count,那麼每次調用count就會執行count[0]加一,並且返回count[0]的值。
綜上,我們可以看出,counter()函數通過不同的參數,得到的函數對象是不一樣的,也就是說,我們能夠通過這種形式將函數和一個參數綁定在一起賦值給一個對象,然後再通過這個對象進行相關的操作。
注意:這裡不能將counter()中的count由列表換成單一變數,否則會報UnboundLocalError錯誤(前提介紹有說明為什麼),但是列表可以。
閉包例二:
def xy_add(x):def adding(y):#根據前面的函數嵌套變數範圍介紹,你應該知道adding()中可以訪問xreturn x + yreturn adding
結果:
>>> myadd = xy_add(3)>>> print myadd(4)7>>> print myadd(3)6
解釋:
xy_add()函數將範圍其嵌套的adding函數對象,當我們給定xy_add()函數參數時,比如給定xi了,並將這個參數下返回的函數對象賦值給myadd後,myadd就是一個帶有特定參數的函數,通過調用myadd(yi),我們就能得到任何一個xi與yi的相加之和。
意義和作用:
說實話,我初學python,還沒怎麼用過閉包,javascript中倒是用過,不過印象已經不深了。這裡摘錄在網上其他人的看法。
1> 函數和對象的根本目的是以某種邏輯方式組織代碼,並提高代碼的可重複使用性。閉包也是一種組織代碼的結構,它同樣提高的代碼的可重複使用性。出處。
2> 當閉包執行完後,仍然能夠保持住當前的運行環境。比如控制棋子運動過程中,閉包可以利用上一次的棋子座標進行運算。出處。
3> 閉包可以根據外部範圍的局部變數來得到不同的結果,這有點像一種類似配置功能的作用,我們可以修改外部的變數,閉包根據這個變數展現出不同的功能。比如有時我們需要對某些檔案的特殊行進行分析,先要提取出這些特殊行。出處。
4> 利於並行運算,類比裝飾器作用等等。
Python閉包的學習