深入理解 Python 中的閉包(Closure)

更新日期: 2024 年 10 月 9 日

在學習 Python 的過程中,你可能會聽到 閉包(Closure) 這個概念。

閉包是函式式編程中的重要特性,它允許函式擁有自己的環境變數。

理解閉包可以讓你寫出更強大、更靈活的代碼。

本文將為新手詳細介紹 Python 中的閉包。


什麼是閉包?

閉包是指一個函式對其外部作用域(環境)變數的引用,即使外部作用域已經結束,該引用仍然存在。

換句話說,閉包允許內部函式「記住」它被創建時的環境。

延伸閱讀:深入理解 Python 的作用域與 LEGB 原則

閉包的條件

要形成閉包,必須滿足以下三個條件:

  1. 必須有一個嵌套函式(函式內部定義函式)。
  2. 內部函式必須引用外部函式的變數。
  3. 外部函式必須返回內部函式。

閉包的基本示例

def outer_function(msg):
    def inner_function():
        print(msg)
    return inner_function

greet = outer_function("Hello, Python!")
greet()  # 輸出:Hello, Python!

在這個例子中:

  • outer_function 是外部函式,接受參數 msg
  • inner_function 是內部函式,打印 msg
  • outer_function 返回 inner_function

即使 outer_function 的執行已經結束,內部函式 inner_function 仍然可以訪問到外部函式的變數 msg。這就是閉包的特性。


函式參數也是變數

在瞭解閉包時,值得注意的是,當我們在函式中建立參數時,其實也是在宣告變數

這些參數在函式的本地作用域內,作為該函式的局部變數存在。

示例

def outer_function(msg):
    def inner_function():
        print(msg)
    return inner_function

在上述範例中:

  • msgouter_function 的參數,同時也是該函式的局部變數。
  • 當我們調用 outer_function("Hello") 時,msg 被賦值為 "Hello",並在 outer_function 的作用域內存在。

參數作為變數的意義

  • 作用域:參數作為局部變數,只在函式內部可見。
  • 閉包捕獲:當內部函式引用了外部函式的參數(變數),這些變數會被閉包捕獲,從而在外部函式執行完畢後,內部函式仍然可以使用它們。

示例:參數在閉包中的角色

def power(exponent):
    def power_of(number):
        return number ** exponent
    return power_of

square = power(2)
cube = power(3)

print(square(5))  # 輸出:25
print(cube(2))    # 輸出:8
  • exponentpower 函式的參數,也是該函式的局部變數。
  • power_of 函式引用了 exponent,因此形成閉包。
  • squarecube 是兩個不同的閉包,分別捕獲了不同的 exponent 值。

注意事項

  • 變數的生命週期:雖然 exponentpower 函式內部定義,但由於被內部函式引用,exponent 的生命週期延長到與內部函式相同。
  • 不可變類型的安全性:像數字、字串等不可變類型,直接被閉包捕獲,不用擔心被意外修改。

深入理解閉包

閉包「記住」外部變數

即使外部函式已經執行完畢,內部函式仍然保留著對外部變數的引用。

def make_multiplier(n):
    def multiplier(x):
        return x * n
    return multiplier

times_three = make_multiplier(3)
print(times_three(5))  # 輸出:15
print(times_three(10)) # 輸出:30
  • make_multiplier 函式創建了一個 multiplier 函式,並將 n 存在閉包中。
  • times_three 是一個閉包,n 的值為 3

多個閉包的獨立性

每次調用外部函式,都會創建一個新的閉包,彼此獨立。

times_two = make_multiplier(2)
times_five = make_multiplier(5)

print(times_two(4))   # 輸出:8
print(times_five(4))  # 輸出:20
  • times_two 的閉包中,n2
  • times_five 的閉包中,n5

為什麼要使用閉包?

1. 數據封裝

閉包可以將數據與函式綁定在一起,實現類似物件導向的封裝效果。

2. 函式式編程

閉包是函式式編程的基石,允許你創建更高階的函式。

3. 減少全域變數

使用閉包可以避免使用全域變數,減少命名空間污染。


實際應用

1. 計數器函式

def counter():
    count = 0
    def increment():
        nonlocal count
        count += 1
        return count
    return increment

c = counter()
print(c())  # 輸出:1
print(c())  # 輸出:2
print(c())  # 輸出:3
  • 使用閉包保存 count 變數,每次調用都會累加。

2. 事件處理器

在 GUI 編程或網頁開發中,閉包常用於創建事件處理器,將狀態與回調函式綁定。

def make_handler(msg):
    def handler():
        print(f"處理訊息:{msg}")
    return handler

handler = make_handler("按鈕被點擊")
handler()  # 輸出:處理訊息:按鈕被點擊

注意事項

1. 避免修改閉包中的可變物件

如果閉包中包含可變對象,應謹慎處理,避免意外修改。

2. 使用 nonlocal 關鍵字

在 Python 3 中,可以使用 nonlocal 關鍵字在閉包中修改外部變數。

def outer():
    x = 0
    def inner():
        nonlocal x
        x += 1
        return x
    return inner

counter = outer()
print(counter())  # 輸出:1
print(counter())  # 輸出:2

結論

閉包是 Python 中強大且靈活的工具,允許函式「記住」創建時的環境。

通過理解閉包,你可以編寫出更高效、更優雅的代碼。

無論是實現數據封裝、創建高階函式,還是減少全域變數的使用,閉包都有著廣泛的應用。


進一步學習

  • 函式式編程:探索 Python 中的函式式編程概念。
  • 裝飾器(Decorator):閉包在裝飾器中的應用。
  • 生成器(Generator):了解另一種強大的函式類型。

Similar Posts