在本文中,您將了解什么是Python閉包,如何定義閉包,以及使用閉包的原因。
在了解閉包是什么之前,我們必須首先了解什么是嵌套函數(shù)和非局部變量。
在另一個(gè)函數(shù)內(nèi)部定義的函數(shù)稱(chēng)為嵌套函數(shù)。嵌套函數(shù)可以訪問(wèn)封閉范圍的變量。
在Python中,默認(rèn)情況下,這些非本地變量是只讀的,并且我們必須將它們明確聲明為非本地變量(使用nonlocal關(guān)鍵字)才能進(jìn)行修改。
以下是訪問(wèn)非局部變量的嵌套函數(shù)的示例。
def print_msg(msg): # 這是外部封閉函數(shù) def printer(): # 這是嵌套函數(shù) print(msg) printer() # 我們執(zhí)行這個(gè)函數(shù) # 輸出: Hello print_msg("Hello")
我們可以看到嵌套函數(shù)printer()能夠訪問(wèn)封閉函數(shù)的非局部變量msg。
在上面的示例中,如果函數(shù)print_msg()的最后一行返回了printer()函數(shù)而不是調(diào)用它,將會(huì)發(fā)生什么? 這意味著功能定義如下。
def print_msg(msg): # 這是外部封閉函數(shù) def printer(): # 這是嵌套函數(shù) print(msg) return printer # 這變了 # 現(xiàn)在,讓我們嘗試調(diào)用此函數(shù)。 # 輸出: Hello another = print_msg("Hello") another()
這很不尋常。
print_msg()函數(shù)用字符串調(diào)用,"Hello"返回的函數(shù)綁定到另一個(gè)名稱(chēng)。在調(diào)用時(shí)another(),盡管我們已經(jīng)完成了print_msg()函數(shù)的執(zhí)行,但仍然記得該消息。
這種將一些數(shù)據(jù)("Hello")附加到代碼上的技術(shù)在Python中稱(chēng)為閉包。
即使變量超出范圍或函數(shù)本身已從當(dāng)前命名空間中刪除,也會(huì)記住封閉范圍中的這個(gè)值。
嘗試在Python Shell中運(yùn)行以下命令以查看輸出。
>>> del print_msg >>> another() Hello >>> print_msg("Hello") Traceback (most recent call last): ... NameError: name 'print_msg' is not defined
從上面的實(shí)例可以看出,在Python中,當(dāng)嵌套的函數(shù)在其封閉的范圍內(nèi)引用一個(gè)值時(shí),我們有一個(gè)閉包。
以下幾點(diǎn)總結(jié)了在Python中創(chuàng)建閉包必須滿足的條件。
我們必須有一個(gè)嵌套函數(shù)(函數(shù)在函數(shù)內(nèi)部)。
嵌套函數(shù)必須引用在封閉函數(shù)中定義的值。
封閉函數(shù)必須返回嵌套函數(shù)。
那么,閉包有什么用呢?
閉包可以避免使用全局值,并提供某種形式的數(shù)據(jù)隱藏。它還可以為該問(wèn)題提供面向?qū)ο蟮慕鉀Q方案。
當(dāng)在一個(gè)類(lèi)中實(shí)現(xiàn)的方法很少(大多數(shù)情況下是一個(gè)方法)時(shí),閉包可以提供另一種更優(yōu)雅的解決方案。但是,當(dāng)屬性和方法的數(shù)量變大時(shí),最好實(shí)現(xiàn)一個(gè)類(lèi)。
這是一個(gè)簡(jiǎn)單的示例,其中閉包可能比定義類(lèi)和創(chuàng)建對(duì)象更可取。
def make_multiplier_of(n): def multiplier(x): return x * n return multiplier # 3的乘數(shù) times3 = make_multiplier_of(3) # 5的乘數(shù) times5 = make_multiplier_of(5) # 輸出: 27 print(times3(9)) # 輸出: 15 print(times5(3)) # 輸出: 30 print(times5(times3(2)))
Python中的裝飾器也大量使用了閉包。
最后,最好指出可以找到封閉在封閉函數(shù)中的值。
所有函數(shù)對(duì)象都有一個(gè)__closure__屬性,如果它是一個(gè)閉包函數(shù),則該屬性返回單元格對(duì)象的元組。參考上面的示例,我們知道times3和times5是閉包函數(shù)。
>>> make_multiplier_of.__closure__ >>> times3.__closure__ (<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)
單元格對(duì)象具有存儲(chǔ)關(guān)閉值的屬性cell_contents。
>>> times3.__closure__[0].cell_contents 3 >>> times5.__closure__[0].cell_contents 5