淺談python閉包

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"))

所以咱們能夠看出,對於實現少許方法的類,咱們能夠用閉包代替。

相關文章
相關標籤/搜索