深入理解 Python 中的閉包(Closure)
更新日期: 2024 年 10 月 9 日
在學習 Python 的過程中,你可能會聽到 閉包(Closure) 這個概念。
閉包是函式式編程中的重要特性,它允許函式擁有自己的環境變數。
理解閉包可以讓你寫出更強大、更靈活的代碼。
本文將為新手詳細介紹 Python 中的閉包。
什麼是閉包?
閉包是指一個函式對其外部作用域(環境)變數的引用,即使外部作用域已經結束,該引用仍然存在。
換句話說,閉包允許內部函式「記住」它被創建時的環境。
延伸閱讀:深入理解 Python 的作用域與 LEGB 原則
閉包的條件
要形成閉包,必須滿足以下三個條件:
- 必須有一個嵌套函式(函式內部定義函式)。
- 內部函式必須引用外部函式的變數。
- 外部函式必須返回內部函式。
閉包的基本示例
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
在上述範例中:
msg
是outer_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
exponent
是power
函式的參數,也是該函式的局部變數。power_of
函式引用了exponent
,因此形成閉包。square
和cube
是兩個不同的閉包,分別捕獲了不同的exponent
值。
注意事項
- 變數的生命週期:雖然
exponent
在power
函式內部定義,但由於被內部函式引用,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
的閉包中,n
為2
。times_five
的閉包中,n
為5
。
為什麼要使用閉包?
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):了解另一種強大的函式類型。