閉包是函數(shù)式編程的重要語法結(jié)構(gòu)革砸,Python是以函數(shù)對象為基礎(chǔ)的喊废,為閉包這一語法結(jié)構(gòu)提供支持氮墨。在學(xué)習(xí)的起初纺蛆,我也不是很理解閉包,經(jīng)過查詢資料和閱讀他人博客后规揪,對閉包有了一點見解桥氏,把它寫出來,需要的朋友可以參考猛铅,如果發(fā)現(xiàn)不足之處字支,希望大家指出。
什么是閉包
閉包(Closure)是詞法閉包(Lexical Closure)的簡稱奸忽,是引用了自由變量(或者稱為環(huán)境變量)的函數(shù)堕伪。這個被引用的自然變量將和這個函數(shù)一同存在,即使離開了創(chuàng)造它的環(huán)境也不例外栗菜。
初步理解:閉包 = 函數(shù) + 自然變量
那么這里的函數(shù)具體是指什么呢欠雌?自然變量又是什么呢?這兩個概念先放在這個疙筹,等到我們有了一定的基礎(chǔ)后理解它就比較容易啦富俄。
內(nèi)嵌函數(shù)
要理解閉包,我們得知道內(nèi)嵌函數(shù)而咆,那么內(nèi)嵌函數(shù)又是什么呢霍比?
內(nèi)嵌函數(shù)是指在一個函數(shù)體內(nèi)創(chuàng)建另一個函數(shù)。新創(chuàng)建的函數(shù)稱為內(nèi)嵌函數(shù)暴备,原來的函數(shù)被稱為外部函數(shù)
def line_conf():
def line(x):
return 2*x + 1
print line(5)
在上面的函數(shù)中悠瞬,line_conf()稱為外部函數(shù),line()稱為內(nèi)嵌函數(shù)涯捻。內(nèi)嵌函數(shù)存在于外部函數(shù)體內(nèi)浅妆,除了外部函數(shù)體內(nèi),其他地方都不能對其調(diào)用汰瘫。
Python中的命名空間(namespace)
在真正地認(rèn)識閉包之前狂打,我們簡單了解一下Python的namespace,有助于我們理解后面的變量混弥。
Python中通過namespace提供重名函數(shù)趴乡、變量等信息的識別对省。共有三種namespace,分別為:
- local namespace: 作用范圍為當(dāng)前函數(shù)或類方法。
- global namespace: 作用范圍為當(dāng)前模塊
- build-in namespace: 作用范圍為所有模塊
當(dāng)函數(shù)晾捏、變量等信息發(fā)生重名時蒿涎,Python會按照"local namespace -> global namespace -> build-in namespace"的順序進(jìn)行搜索用戶所需元素,并且以第一個找到此元素的namespace為準(zhǔn)惦辛。
Python中的內(nèi)建函數(shù)locals()和globals()可以查看不同namespace中定義的元素劳秋。
內(nèi)嵌函數(shù)的深入
函數(shù)對象的作用域與def所在的層次相同,函數(shù)是一個對象胖齐,可以作為某個函數(shù)的返回結(jié)果玻淑。
下面這段代碼是接近閉包的一個構(gòu)造:
# eg1
>>> def line_conf():
def line(x):
return 2*x + 1
return line
>>> my_line = line_conf()
>>> my_line(5)
11
閉包的創(chuàng)建
# eg2
>>> def line_conf():
'this is ture closure.'
b = 15
def line(x):
return 2*x + 1
return line
>>> my_line = line_conf()
>>> my_line(3)
7
通過對比eg2和eg1的代碼發(fā)現(xiàn),在函數(shù)line_conf中,eg2比eg1多了一個變量呀伙。其實补履,這個變量就是閉包中一個重要的組成:自由變量。
自由變量:定義在外部函數(shù)內(nèi)的剿另,但由內(nèi)嵌函數(shù)引用或使用的變量箫锤。
即閉包 = 自由變量 + 內(nèi)嵌函數(shù)
重新審視下上面的代碼,函數(shù)line_conf()是外部函數(shù)雨女,變量b是自由變量谚攒,函數(shù)line()是內(nèi)嵌函數(shù),只能在外部函數(shù)line_conf()體內(nèi)調(diào)用函數(shù)line()氛堕。line()函數(shù)訪問了non_local的自由變量"b",自由變量"b"并沒有隨著外部函數(shù)的退出而銷毀馏臭,反而是生命周期得到了延長。
closure屬性
在Python中岔擂,我們可以通過函數(shù)對象的closure屬性查看閉包的一些細(xì)節(jié)位喂,也可以理解為什么自由變量沒有隨著外部函數(shù)的退出而銷毀。
>>> def line_conf():
b =15
def line(x):
return 2 * x + b
return line
>>> my_line = line_conf()
>>> print my_line.__closure__
(<cell at 0x02AC2FF0: int object at 0x0247A230>,)
>>> print my_line.__closure__[0].cell_contents
15
通過調(diào)試結(jié)果看出乱灵,closure里包含一個元組塑崖,這個元組的每個元素都是cell類型的對象,第一個cell包含的就是我們創(chuàng)建閉包時的自由變量b的取值痛倚。
閉包的總結(jié)
閉包是函數(shù)式編程的重要語法結(jié)構(gòu)规婆,函數(shù)式編程和面向過程以及面向?qū)ο缶幊桃粯佣际蔷幊谭妒剑嫦蜻^程編程中的函數(shù)蝉稳,面向過程編程中的對象和閉包的相同點是:以某種邏輯方式組織代碼抒蚜,實現(xiàn)代碼的可重復(fù)性使用。
閉包的特性:
- 閉包函數(shù)必須有內(nèi)嵌函數(shù)
- 內(nèi)嵌函數(shù)需要引用外部函數(shù)中的變量
- 閉包函數(shù)必須返回內(nèi)嵌函數(shù)
注意:我們在寫閉包時耘戚,不要在內(nèi)嵌函數(shù)中對自由變量進(jìn)行賦值操作嗡髓,至少在Python2.x中不可以。會出現(xiàn)UnbundLocalError錯誤收津。