淺談Python閉包

閉包是在其詞法上下文中引用了自由變量的函數。閉包

通俗地說,就是函數嵌套(後續稱之爲外層函數)另一個函數(後續稱之爲內層函數),在內層函數中,引用外層函數的變量,每次對內層函數的調用,外層函數變量的值都會進行保持。函數

用個簡單的例子來講明,使用閉包實現一個函數,求全部傳入的數字的平均值。spa

def averager():
    """
    閉包實現求平均值的例子
    每次傳入一個數字,返回全部傳入的數字的平均值
    :return:
    """
    count = 0
    total = 0.0

    def _averager(value):
        nonlocal total, count
        total += value
        count += 1
        average = total/count
        return average

    return _averager

# 調用外層函數averager,獲得內層函數_averager的對象,賦值給avg
# 此時閉包造成,外層函數的變量total, count, average會被保持
avg = averager()
# 每次調用內層函數avg時,外層函數變量的值都會被記住
# 第一次調用,外層函數的變量count=1, total=10.0, average=10.0
print(avg(10))
# 10.0
# 第二次調用,外層函數變量的值不會重置,仍然保持上次調用的結果
# 此時 count=2, total=30.0, average=15.0
print(avg(20))
# 15.0
# 第三次調用,外層函數變量的值仍然會保持上次調用的結果
# 此時 count=3, total=36.0, average=12.0
print(avg(6))
# 12.0

上面的例子中,最重要的部分在調用外層函數averager時,須要返回內層函數的對象_averager,注意這裏的return _averager沒有括號,並非調用內層函數,而是返回內層函數的對象。code

avg_cls = Averager()當調用外層函數,返回內層函數對象時,即造成閉包。對象

avg(10)每次對內層函數的調用,外層函數中的變量的值,都會被記住,即保持上次調用的結果。blog

若是不容易理解,能夠用類實例實現相似上面閉包的功能,實際執行中,由於類實例self的參與,運行速度要略慢於閉包的實現。it

# 以類實現相似上面閉包的功能
class Averager:

    def __init__(self):
        self.count = 0
        self.total = 0.0

    def __call__(self, value):
        self.total += value
        self.count += 1
        average = self.total/self.count
        return average

avg_cls = Averager()

print(avg_cls(10))
# 10.0
print(avg_cls(20))
# 15.0
print(avg_cls(6))
# 12.0

上面這個使用類實現的例子中,變量count和total,都保存在類實例avg_cls中,每次對類實例的調用,都會調用__call__方法計算平均值,由於是同一個實例,因此count和total的值都會存儲在實例中。class

閉包也是相似的效果,變量count和total也是存儲在外層函數的上下文中,因此每次對內層函數調用,外層函數的變量不會被銷燬,而是一直保持初始值(未調用內層函數時)或上次調用的結果(每次調用內層函數後,對count和total的修改都會被存儲)。變量

相關文章
相關標籤/搜索