1.何爲閉包javascript
在百度百科裏面,看到了這樣的定義:java
閉包就是可以讀取其餘函數內部變量的函數。例如在javascript中,只有函數內部的子函數才能讀取局部變量,因此閉包能夠理解成「定義在一個函數內部的函數「。在本質上,閉包是將函數內部和函數外部鏈接起來的橋樑。閉包
閉包包含自由(未綁定到特定對象)變量,這些變量不是在這個代碼塊內或者任何全局上下文中定義的,而是在定義代碼塊的環境中定義(局部變量)。「閉包」 一詞來源於如下二者的結合:要執行的代碼塊(因爲自由變量被包含在代碼塊中,這些自由變量以及它們引用的對象沒有被釋放)和爲自由變量提供綁定的計算環境(做用域)。函數
從上面兩段話咱們能夠更好的理解:在一個外函數中定義了一個內函數,內函數裏運用了外函數的臨時變量,而且外函數的返回值是內函數的引用。這樣就構成了一個閉包。spa
下面是一個閉包的小例子:code
def outer(): a = 6 def inner(): b = 8 print(a) print(b) return inner if __name__ == '__main__': res = outer() res() 執行結果: >>>6 >>>8
咱們能夠看到,當調用外部函數outer()時,會返回一個內部函數的引用res,再經過res()執行了內部函數,返回打印結果6和8orm
這上面的例子中,a做爲外部函數outer()的局部變量,其被分配的內存在外部函數執行後應被釋放,但在外部函數執行後,發現本身的局部變量將被內部函數引用,就把這個變量綁定給了內部函數,而後再本身結束。此處的outer()的局部變量a稱之爲自由變量,所以,對於內部函數inner()而言,其自由變量和局部變量咱們能夠經過下面的命令獲得:對象
>>> res.__code__.co_freevars ('a',) >>> res.__code__.co_varnames ('b',)
上面咱們說到,外部函數的局部變量a被綁定到了內部函數,咱們能夠經過返回函數res的__closure__屬性找到它blog
>>> res.__closure__[0].cell_contents 6
2.閉包的變量ip
def outer(): a = 6 def inner(): b = 8 a += 1 print(a) print(b) return inner if __name__ == '__main__': res = outer() res() 執行函數會報錯:UnboundLocalError: local variable 'a' referenced before assignment
這個報錯的意思是內部函數引用的變量a在賦值前已經被引用,緣由在於變量a是數字,不可變類型,a += 1至關於在內部函數中建立了局部變量a = a + 1,這樣a再也不是自由變量,也不存在閉包。
這裏咱們能夠嘗試下外部函數的局部變量爲可變類型的狀況:
def outer(): a = [1,2,3] def inner(): b = 8 a = [4,5] print(a) print(b) return inner if __name__ == '__main__': res = outer() res() 執行結果: [4, 5] 8
能夠看到,當a爲可變類型時,函數能夠正確執行。那麼當a爲不可變類型但又想在內部函數中改變a的值該怎麼作,答案是利用nonlocal關鍵字。
def outer(): a = 6 def inner(): nonlocal a b = 8 a += 1 print(a) print(b) return inner if __name__ == '__main__': res = outer() res() 執行結果: 7 8
3.閉包的應用
下面模擬一個nba球員信息的小例子
首先用普通函數實現:
class PlayerInfo(): def __init__(self, name): self.name = name def position(self, position): return "{} is {}".format(self.name, position) curry = PlayerInfo("Stephen Curry") print(curry.position("PG")) lbj = PlayerInfo("Lebron James") print(lbj.position("SF")) 執行後: Stephen Curry is PG Lebron James is SF
下面用閉包函數來實現
def get_name(name): def get_position(position): return "{} is {}".format(name,position) return get_position if __name__ == "__main__": player01 = get_name("Stephen Curry") print(player01("PG")) player02 = get_name("Lebron James") print(player02("SF"))
所以咱們能夠看出,對於實現少許方法的類,咱們能夠用閉包代替。