from: https://serholiu.com/python-closures html
閉包這個概念在 JavaScript 中討論和使用得比較多,不過在 Python 中卻不是那麼顯而易見,之因此說「不是那麼」,是由於即便用到了,也沒用注意到而已,好比定義一個 Decorator 時,就已經用到閉包了。網上對閉包的各類解釋,感受很是晦澀,在這裏談談個人淺顯認識:要造成閉包,首先得有一個嵌套的函數,即函數中定義了另外一個函數,閉包則是一個集合,它包括了外部函數的局部變量,這些局部變量在外部函數返回後也繼續存在,並能被內部函數引用。python
這是個常常使用到的例子,定義一個函數 generate_power_func
,它返回另外一個函數,如今閉包造成的條件已經達到。linux
1 2 3 4 5 6 |
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 |
對於內部函數 nth_power
,它能引用到外部函數的局部變量 n
,並且即便 generate_power_func
已經返回。把這種現象就稱爲閉包。具體使用一下。shell
1 2 3 4 5 |
>>> raised_to_4 = generate_power_func(4)id(n): 246F770id(nth_power): 2C090C8>>> repr(raised_to_4)'<function nth_power at 0x2c090c8>' |
從結果能夠看出,當 generate_power_func(4)
執行後, 建立和返回了 nth_power
這個函數對象,內存地址是 0x2C090C8,而且發現 raised_to_4
和它的內存地址相同,即 raised_to_4
只是這個函數對象的一個引用。先在全局命名空間中刪除 generate_power_func
,再試試會出現什麼結果。設計模式
1 2 3 |
>>> del generate_power_func>>> raised_to_4(2)16 |
啊哈,竟然沒出現錯誤, nth_power
是怎麼知道 n
的值是 4,並且如今 generate_power_func
甚至都不在這個命名空間了。對,這就是閉包的做用,外部函數的局部變量能夠被內部函數引用,即便外部函數已經返回了。閉包
如今知道閉包是怎麼一回事了,那就到看看閉包究竟是怎麼回事的時候了。Python 中函數也是對象,因此函數也有不少屬性,和閉包相關的就是 __closure__
屬性。__closure__
屬性定義的是一個包含 cell 對象的元組,其中元組中的每個 cell 對象用來保存做用域中變量的值。ide
1 2 3 4 5 6 |
>>> 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_contents4 |
就如剛纔所說,在 raised_to_4
的 __closure__
屬性中有外部函數變量 n
的引用,經過內存地址能夠發現,引用的都是同一個 n
。若是沒用造成閉包,則 __closure__
屬性爲 None
。對於 Python 具體是如何實現閉包的,能夠查看 Python閉包詳解,它經過分析 Python 字節碼來說述閉包的實現。函數
閉包特性有着很是多的做用,不過都是須要時纔會不經意的用上,不要像使用設計模式同樣去硬套這些法則。這篇文章按照本身的理解翻譯至 Python Closures Explained,可能和原文有些不一樣之處,若有疑惑,請查看原文。附上一些參考資料。spa
閉包的概念、形式與應用: 能夠從其中瞭解閉包的應用翻譯
Python閉包詳解:從字節碼出發瞭解 Python 閉包的實現機制
理解Javascript的閉包: 從 Javascript 的閉包中瞭解一些閉包特性,能夠和 Python 做下對比