閉包不好理解,所以先從示例說起,
假設我們需要計算平均值,這些值會從外層傳遞進來,然后被保存在內部,
(1) 非閉包方式實作
class Averager():
def __init__(self):
self.series = []
def __call__(self, new_value):
self.series.append(new_value)
total = sum(self.series)
return total / len(self.series)
avg = Averager()
logging.info('avg(10) -> %s', avg(10))
logging.info('avg(20) -> %s', avg(20))
logging.info('avg(30) -> %s', avg(30))
運行結果:

- 非閉包方式定義了一個類,名為 Averager,然后在初始化方法中為該類定義了一個陣列 series,用于保存傳入進來的數值,
- 接著使用
__call__使得該類實體物件可以像呼叫普通函式那樣,以“物件名()”的形式被使用1,它接收一個引數作為需要計算的新數值,內部被保存在 series 陣列中,
(2) 閉包方式實作
這個平均數計算方式可以采用函式式編程方式來實作,
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total / len(series)
return averager
avg = make_averager()
logging.info('avg(10) -> %s', avg(10))
logging.info('avg(20) -> %s', avg(20))
logging.info('avg(30) -> %s', avg(30))
運行結果與上例相同,
- 我們定義了一個 make_averager 函式,其內部又定義了一個名為 averager(new_value) 的函式,里面是計算平均數的演算法;
- 傳入的引數被保存在外層的 series 陣列中;
- 最后回傳這個內部函式,

黃色區域表示產生閉包現象的代碼段,其中的 series 陣列是自由變數(free variable),不受內部函式 averager 的影響,所以可以保存所有傳入的變數值,
內部函式 averager 的區域作用域內的變數,都會在函式被呼叫后失效,
通過 __code__ 屬性可以看到 avg 函式中的變數名稱,__code__ 屬性是編譯后的函式定義體,
logging.info('avg.__code__.co_varnames -> %s', avg.__code__.co_varnames)
logging.info('avg.__code__.co_freevars -> %s', avg.__code__.co_freevars)
運行結果:

其中 co_varnames 表示區域變數;co_freevars 表示自由變數,這與我們之前所描述的閉包場景一致,
閉包中的自由變數值保存在 avg 函式的 __closure__ 屬性中,它是一組 cell 物件串列,每個 cell 物件與 co_freevars 串列中的名稱一一對應:
INFO - avg.__closure__ -> (<cell at 0x000002A8AF736D38: list object at 0x000002A8AF9C7DC8>,)
INFO - avg.__closure__[0].cell_contents -> [10, 20, 30]
運行結果:

closure 翻譯過來就是閉包,
通過閉包,我們可以保留住定義的自由變數的值,這樣函式呼叫后,我們仍然可以使用這些變數,
- Python
__call__()方法(詳解版). - Luciano Ramalho (作者),安道,吳珂 (譯者).流暢的Python[M].人民郵電出版社,2017:312-315.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/258959.html
標籤:python
上一篇:Python語言之迭代器與生成器
