🧠 什么是閉包?
閉包是一個函數對象,它不僅記住它的代碼邏輯,還記住了定義它時的自由變量(即非全局也非局部,但被內部函數引用的變量)。即使外部函數已經執行完畢,這些自由變量的值仍然保存在內存中,可以通過閉包訪問和使用。
簡單來說:
-
當一個嵌套的函數引用了其外部函數中的變量,并且這個嵌套函數可以在其外部函數之外被調用時,就形成了一個閉包。
? 閉包的基本結構
def?outer_function(x):def?inner_function(y):return?x?+?yreturn?inner_function
closure?=?outer_function(10)
print(closure(5))??#?輸出:?15
在這個例子中,inner_function
是 outer_function
的內部函數,并且引用了 outer_function
的參數 x
。當 outer_function
返回 inner_function
時,盡管 outer_function
已經執行結束,但是 inner_function
依然保留了對外部變量 x
的引用,這就形成了一個閉包。
? 閉包的工作原理
閉包允許你在一個函數內創建并返回另一個函數,而后者能夠“記住”前者內部的變量狀態。這種特性對于需要保持某些狀態的應用場景特別有用,比如計數器等。
示例:簡單的計數器
def?make_counter():count?=?0def?counter():nonlocal?count??#?使用?nonlocal?聲明?count?是外部函數中的變量count?+=?1return?countreturn?counter
counter?=?make_counter()
print(counter())??#?輸出:?1
print(counter())??#?輸出:?2
print(counter())??#?輸出:?3
在這個例子中,make_counter
函數返回了一個閉包 counter
,它可以不斷增加并返回一個計數值。注意這里使用了 nonlocal
關鍵字來表明我們要修改的是外部函數中的 count
變量。
? 為什么使用閉包?
-
封裝:閉包可以幫助我們封裝一些數據,避免全局變量的污染。
-
狀態保持:通過閉包,我們可以輕松地實現帶有狀態的函數。
-
減少命名沖突:由于閉包內部使用的變量對外部不可見,因此減少了命名沖突的風險。
? 閉包與裝飾器
閉包的一個常見應用場景就是作為裝飾器的基礎。裝飾器本質上也是一個閉包,它接受一個函數作為輸入,并返回一個新的函數,通常用于在不改變原函數的情況下添加額外的功能。
示例:簡單的裝飾器
def?decorator(func):def?wrapper():print("Something?is?happening?before?the?function?is?called.")func()print("Something?is?happening?after?the?function?is?called.")return?wrapper
@decorator
def?say_hello():print("Hello!")
say_hello()
輸出:
Something is happening before the function is called.Hello!Something is happening after the function is called.
?? 注意事項
-
可變對象:如果閉包中引用的對象是可以變的(如列表),那么對這個對象的任何更改都會反映在閉包中。
-
性能問題:雖然閉包很強大,但在某些情況下可能會導致性能問題或增加程序復雜度,所以應謹慎使用。
📌 總結
-
閉包是由函數及其相關引用環境組合而成的實體。
-
它允許函數“記住”并訪問其定義范圍內的變量,即使那個函數在其定義的作用域外被調用。
-
閉包廣泛應用于各種編程模式中,如裝飾器、工廠函數等。