轉(zhuǎn)自: 淺顯理解 Python 閉包
閉包這個概念在 JavaScript 中討論和使用得比較多,不過在 Python 中卻不是那么顯而易見,之所以說“不是那么”,是因為即使用到了,也沒用注意到而已,比如定義一個 Decorator 時,就已經(jīng)用到閉包了。網(wǎng)上對閉包的各種解釋,感覺非常晦澀,在這里談?wù)勎业臏\顯認識:要形成閉包,首先得有一個嵌套的函數(shù),即函數(shù)中定義了另一個函數(shù),閉包則是一個集合,它包括了外部函數(shù)的局部變量,這些局部變量在外部函數(shù)返回后也繼續(xù)存在,并能被內(nèi)部函數(shù)引用。
舉個例子
這是個經(jīng)常使用到的例子,定義一個函數(shù)
generate_power_func
,它返回另一個函數(shù),現(xiàn)在閉包形成的條件已經(jīng)達到。
def generate_power_func(n):
print "id(n): %X" % id(n)
def nth_power(x):
return x**n
print "id(nth_power): %X" % id(nth_power)
return nth_power
對于內(nèi)部函數(shù)nth_power
,它能引用到外部函數(shù)的局部變量n
,而且即使generate_power_func
已經(jīng)返回。把這種現(xiàn)象就稱為閉包。具體使用一下。
>>> raised_to_4 = generate_power_func(4)
id(n): 246F770
id(nth_power): 2C090C8
>>> repr(raised_to_4)
'<function nth_power at 0x2c090c8>'
從結(jié)果可以看出,當generate_power_func(4)
執(zhí)行后, 創(chuàng)建和返回了nth_power
這個函數(shù)對象,內(nèi)存地址是 0x2C090C8,并且發(fā)現(xiàn)raised_to_4
和它的內(nèi)存地址相同,即raised_to_4
只是這個函數(shù)對象的一個引用。先在全局命名空間中刪除generate_power_func
,再試試會出現(xiàn)什么結(jié)果。
>>> del generate_power_func
>>> raised_to_4(2)
16
啊哈,居然沒出現(xiàn)錯誤,nth_power
是怎么知道n
的值是 4,而且現(xiàn)在generate_power_func
甚至都不在這個命名空間了。對,這就是閉包的作用,外部函數(shù)的局部變量可以被內(nèi)部函數(shù)引用,即使外部函數(shù)已經(jīng)返回了。
__closure__
屬性和 cell 對象
現(xiàn)在知道閉包是怎么一回事了,那就到看看閉包到底是怎么回事的時候了。Python 中函數(shù)也是對象,所以函數(shù)也有很多屬性,和閉包相關(guān)的就是__closure__
屬性。__closure__
屬性定義的是一個包含 cell 對象的元組,其中元組中的每一個 cell 對象用來保存作用域中變量的值。
>>> raised_to_4.__closure__
(<cell at 0x2bf4ec0: int object at 0x246f770>,)
>>> type(raised_to_4.__closure__[0])
<type 'cell'>
>>> raised_to_4.__closure__[0].cell_contents
4
就如剛才所說,在raised_to_4
的__closure__
屬性中有外部函數(shù)變量n
的引用,通過內(nèi)存地址可以發(fā)現(xiàn),引用的都是同一個n
。如果沒用形成閉包,則__closure__
屬性為None
。對于 Python 具體是如何實現(xiàn)閉包的,可以查看Python閉包詳解,它通過分析 Python 字節(jié)碼來講述閉包的實現(xiàn)。
最后總結(jié)
閉包特性有著非常多的作用,不過都是需要時才會不經(jīng)意的用上,不要像使用設(shè)計模式一樣去硬套這些法則。這篇文章按照自己的理解翻譯至
Python Closures Explained,可能和原文有些不同之處,如有疑惑,請查看原文。附上一些參考資料。
- 閉包的概念、形式與應(yīng)用: 可以從其中了解閉包的應(yīng)用
- Python閉包詳解:從字節(jié)碼出發(fā)了解 Python 閉包的實現(xiàn)機制
- 理解Javascript的閉包: 從 Javascript 的閉包中了解一些閉包特性,可以和 Python 作下對比