Python里閉包原理 嵌套函數中閉包變量的綁定機制解析

閉包python中內部函數捕獲外部函數作用域變量并持續訪問的機制。具體來說,閉包是一個函數加上其引用環境,即使外部函數執行完畢,內部函數仍能記住并訪問外部變量。例如,在outer函數中定義的inner函數可以訪問outer的變量x,即使outer已執行完成。閉包變量是后期綁定(late binding),即調用時才查找變量當前值,這可能導致多個閉包引用同一個變量而得到相同最終值的問題,如make_multipliers例子中所有Lambda都返回8。解決方法是通過默認參數立即綁定值。閉包變量屬于嵌套函數外層作用域(e層級),讀取無需聲明,修改則需使用nonlocal關鍵字。閉包常用于狀態保持、封裝數據和裝飾器,但也需注意內存釋放、變量綁定問題及多層嵌套帶來的效率影響。可通過__closure__屬性檢查閉包內容。掌握閉包的作用域規則與綁定機制是寫出穩定代碼的關鍵。

python中,閉包是一個函數加上它所處環境的引用。說得更具體一點,就是內部函數記住了外部函數作用域中的變量值,即使外部函數已經執行完畢,這些變量仍然可以被訪問。

這種機制常用于裝飾器、回調函數等場景,理解閉包變量的綁定機制,能幫助我們寫出更穩定、可預測的代碼。


什么是閉包?

閉包(Closure)指的是一個函數捕獲了其定義時所在的環境變量,并且即使這個環境不再存在,它也能繼續訪問這些變量。

舉個簡單例子:

立即學習Python免費學習筆記(深入)”;

def outer(x):     def inner():         print(x)     return inner  closure = outer(10) closure()  # 輸出 10

在這個例子中,inner 是一個閉包,因為它“記住”了 outer 函數中的變量 x。即使 outer 已經執行完,返回的 inner 還是可以訪問到 x 的值。


閉包變量是按引用還是按值綁定的?

這是很多人容易混淆的地方:閉包變量在 Python 中是后期綁定的(late binding),也就是說,它不是在定義的時候固定住變量的值,而是在調用時才去查找變量的當前值。

來看一個經典例子:

def make_multipliers():     return [lambda x: x * i for i in range(5)]  for m in make_multipliers():     print(m(2))

你可能會以為輸出是 0 2 4 6 8,但實際輸出是 8 8 8 8 8。

為什么?因為在列表推導式中,每個 lambda 都引用了同一個變量 i,而當 make_multipliers() 執行完后,i 最終停留在了 4,所以所有閉包中的 i 都指向最后的值。

要解決這個問題,通常的做法是在定義時立即綁定值,比如通過默認參數“凍結”當前值:

def make_multipliers():     return [lambda x, i=i: x * i for i in range(5)]

這樣每個 lambda 都保存了當時循環中的 i 值。


嵌套函數中變量的作用域規則

在嵌套函數中,Python 使用 LEGB 規則 來查找變量:

  • L: Local(本地作用域)
  • E: Enclosing(外層嵌套函數作用域)
  • G: Global(全局作用域)
  • B: Built-in(內置作用域)

閉包變量屬于 E 層級,也就是外層函數作用域中的變量。

如果我們在內層函數中修改這些變量,需要注意以下幾點:

  • 如果只是讀取,不需要做任何聲明。
  • 如果想修改,需要使用 nonlocal 關鍵字(適用于嵌套函數)或 global(如果變量在全局)。

例如:

def outer():     count = 0     def inner():         nonlocal count         count += 1         print(count)     return inner  counter = outer() counter()  # 輸出 1 counter()  # 輸出 2

如果沒有 nonlocal,Python 會認為你在給局部變量 count 賦值,從而引發錯誤。


實際應用與注意事項

閉包在很多高級技巧中都有使用,比如:

  • 模擬狀態保持(如計數器)
  • 封裝數據,避免污染全局空間
  • 構造裝飾器邏輯

不過要注意幾個點:

  • 閉包會持有對外部變量的引用,可能導致內存無法釋放。
  • 多層嵌套函數中,變量查找效率略低(雖然一般影響不大)。
  • 循環中生成閉包時,注意 late binding 的問題。

如果你不確定某個變量是否被正確捕獲,可以用 __closure__ 屬性查看閉包內容:

def outer():     x = 10     def inner():         return x     return inner  f = outer() print(f.__closure__) # 輸出類似 (<cell at 0x...: int object at 0x...>,)

基本上就這些。閉包是個強大又容易出錯的功能,掌握好變量綁定機制和作用域規則,才能用得更穩。

? 版權聲明
THE END
喜歡就支持一下吧
點贊6 分享