Python: 攜帶狀態的閉包

發表於2016-11-23

在 Python 中,函式也是一個物件。因此,我們在定義函式時,可以再巢狀定義一個函式,並將該巢狀函式返回,比如:

上面的程式碼中,函式 make_pow 裡面又定義了一個內部函式 inner_func,然後將該函式返回。因此,我們可以使用 make_pow 來生成另一個函式:

我們還注意到,內部函式 inner_func 引用了外部函式 make_pow 的自由變數 n,這也就意味著,當函式 make_pow 的生命週期結束之後,n 這個變數依然會儲存在 inner_func 中,它被 inner_func 所引用。

像上面這種情況,一個函式返回了一個內部函式,該內部函式引用了外部函式的相關引數和變數,我們把該返回的內部函式稱為閉包(Closure)

在上面的例子中,inner_func 就是一個閉包,它引用了自由變數 n

閉包的作用

  • 閉包的最大特點就是引用了自由變數,即使生成閉包的環境已經釋放,閉包仍然存在;
  • 閉包在執行時可以有多個例項,即使傳入的引數相同,比如:

  • 利用閉包,我們還可以模擬類的例項。

這裡構造一個類,用於求一個點到另一個點的距離:

用閉包來實現:

可以看到,結果是一樣的,但使用閉包實現比使用類更加簡潔。

常見誤區

閉包的概念很簡單,但實現起來卻容易出現一些誤區,比如下面的例子:

在該例子中,我們在每次 for 迴圈中建立了一個函式,並將它存到 funcs 中。現在,呼叫上面的函式,你可能認為返回結果是 1, 2, 3,事實上卻不是:

為什麼呢?原因在於上面的函式 f 引用了變數 i,但函式 f 並非立刻執行,當 for 迴圈結束時,此時變數 i 的值是3,funcs 裡面的函式引用的變數都是 3,最終結果也就全為 3。

因此,我們應儘量避免在閉包中引用迴圈變數,或者後續會發生變化的變數

那上面這種情況應該怎麼解決呢?我們可以再建立一個函式,並將迴圈變數的值傳給該函式,如下:

小結

  • 閉包是攜帶自由變數的函式,即使建立閉包的外部函式的生命週期結束了,閉包所引用的自由變數仍會存在。
  • 閉包在執行可以有多個例項。
  • 儘量不要在閉包中引用迴圈變數,或者後續會發生變化的變數。

參考資料

相關文章