如何理解及使用Python閉包
一、Python 中的作用域規(guī)則和嵌套函數(shù)
每當(dāng)執(zhí)行一個(gè)函數(shù)時(shí),就會(huì)創(chuàng)建一個(gè)新的局部命名空間,它表示包含函數(shù)體內(nèi)分配的函數(shù)參數(shù)和變量名的局部環(huán)境。我們可以將名稱空間看作一個(gè)字典,其中鍵是對(duì)象名稱,值是對(duì)象本身。
解析名稱時(shí),解釋器首先搜索本地命名空間。如果不存在匹配,則搜索全局名稱空間,該名稱空間是定義函數(shù)的模塊。如果仍然沒(méi)有找到匹配項(xiàng),則在引發(fā) NameError 異常之前最終檢查內(nèi)置名稱空間。下圖說(shuō)明了這一點(diǎn):
讓我們考慮下面的例子:
age = 27 def birthday(): age = 28 birthday() print(age) # age will still be 27 >> 27
當(dāng)變量在函數(shù)內(nèi)部賦值時(shí),它們總是綁定到函數(shù)的本地名稱空間; 因此,函數(shù)體中的變量 age 指的是一個(gè)包含值28的全新對(duì)象,而不是外部變量??梢允褂萌终Z(yǔ)句更改此行為。下面的示例強(qiáng)調(diào)了這一點(diǎn):
age = 27 name = "Sarah" def birthday(): global age # 'age' is in global namespace age = 28 name = "Roark" birthday() # age is now 28. name will still be "Sarah"
Python 也支持嵌套函數(shù)定義(函數(shù)內(nèi)部的函數(shù)):
def countdown(start): # This is the outer enclosing function def display(): # This is the nested function n = start while n > 0: n-=1 print('T-minus %d' % n) display() # We execute the function countdown(3) >>> T-minus 3 T-minus 2 T-minus 1
二、定義閉包函數(shù)
在上面的示例中,如果函數(shù) countdown()的最后一行返回了 display 函數(shù)而不是調(diào)用它,會(huì)發(fā)生什么情況?這意味著該函數(shù)的定義如下:
def countdown(start): # This is the outer enclosing function def display(): # This is the nested function n = start while n > 0: n-=1 print('T-minus %d' % n) return display # Now let's try calling this function. counter1 = countdown(2) counter1() >>> T-minus 2 T-minus 1
使用值2調(diào)用 countdown()函數(shù),并將返回的函數(shù)綁定到名稱 counter1。在執(zhí)行 counter1()時(shí),它使用最初提供給 countdown ()的 start 值。因此,在調(diào)用 counter1()時(shí),盡管我們已經(jīng)執(zhí)行了 count1()函數(shù),但仍然記住這個(gè)值。
這種將一些數(shù)據(jù)(本例中為2)附加到代碼的技術(shù)在 Python 中稱為閉包。
即使變量超出范圍或函數(shù)本身從當(dāng)前名稱空間中移除,也會(huì)記住封閉范圍中的這個(gè)值。我們可以嘗試下面的代碼來(lái)確認(rèn):
>>> del countdown >>> counter1() T-minus 2 T-minus 1 >>> countdown(2) Traceback (most recent call last): ... NameError: name 'countdown' is not defined
三、何時(shí)使用閉包?
當(dāng)一個(gè)類(lèi)中實(shí)現(xiàn)的方法很少(大多數(shù)情況下只有一個(gè)方法)時(shí),閉包可以提供一個(gè)替代的、更優(yōu)雅的解決方案。此外,如果我們希望根據(jù)延遲或延遲計(jì)算的概念編寫(xiě)代碼,閉包和嵌套函數(shù)特別有用。下面是一個(gè)例子:
from urllib.request import urlopen def page(url): def get(): return urlopen(url).read() return get
在上面的示例中,page ()函數(shù)實(shí)際上并不執(zhí)行任何計(jì)算。相反,它只是創(chuàng)建并返回一個(gè)函數(shù) get () ,該函數(shù)在調(diào)用 web 頁(yè)面時(shí)獲取頁(yè)面內(nèi)容。因此,在 get ()中執(zhí)行的計(jì)算實(shí)際上被延遲到計(jì)算 get ()時(shí)程序中的某個(gè)后續(xù)點(diǎn)。例如:
>>> url1 = page("http://www.google.com") >>> url2 = page("http://www.bing.com") >>> url1 <function page.<locals>.get at 0x10a6054d0> >>> url2 <function page.<locals>.get at 0x10a6055f0> >>> gdata = url1() # Fetches http://www.google.com >>> bdata = url2() # Fetches http://www.bing.com >>>
可以找到閉包函數(shù)中包含的值。
所有函數(shù)對(duì)象都有一個(gè) _closure_ 屬性,如果它是一個(gè)閉包函數(shù),那么這個(gè)屬性將返回一組單元格對(duì)象。根據(jù)上面的例子,我們知道 url1和 url2是閉包函數(shù)。
>>> page.__closure__ # Returns None since not a closure >>> url1.__closure__ (<cell at 0x10a5f1250: str object at 0x10a5f3120>,)
單元格對(duì)象具有存儲(chǔ)關(guān)閉值的屬性 cell_contents。
>>> url1.__closure__[0].cell_contents 'http://www.google.com' >>> url2.__closure__[0].cell_contents 'http://www.bing.com'
四、總結(jié)
當(dāng)嵌套函數(shù)引用其封閉范圍中的值時(shí),可以定義 Python 中的閉包。閉包提供了某種形式的數(shù)據(jù)隱藏。閉包還可以是一種高效的方法,可以在一系列函數(shù)調(diào)用之間保持狀態(tài)。用 Python 創(chuàng)建一個(gè)閉包函數(shù):
- 我們必須有一個(gè)嵌套的函數(shù)
- 嵌套函數(shù)必須引用封閉函數(shù)中定義的值
- 封閉函數(shù)必須返回嵌套函數(shù)
到此這篇關(guān)于如何理解及使用Python閉包的文章就介紹到這了,更多相關(guān)Python閉包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在python中利用GDAL對(duì)tif文件進(jìn)行讀寫(xiě)的方法
今天小編就為大家分享一篇在python中利用GDAL對(duì)tif文件進(jìn)行讀寫(xiě)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-11-11Django框架基礎(chǔ)認(rèn)證模塊auth應(yīng)用示例
這篇文章主要為大家介紹了Django框架認(rèn)證模塊auth示例應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03Python-Tkinter Text輸入內(nèi)容在界面顯示的實(shí)例
今天小編就為大家分享一篇Python-Tkinter Text輸入內(nèi)容在界面顯示的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07Python模塊對(duì)Redis數(shù)據(jù)庫(kù)的連接與使用講解
這篇文章主要介紹了Python模塊對(duì)Redis數(shù)據(jù)庫(kù)的連接與使用,通過(guò)實(shí)例代碼給大家介紹了Python連接Redis數(shù)據(jù)庫(kù)方法,Python使用連接池連接Redis數(shù)據(jù)庫(kù)方法,感興趣的朋友跟隨小編一起看看吧2021-07-07kNN算法python實(shí)現(xiàn)和簡(jiǎn)單數(shù)字識(shí)別的方法
這篇文章主要介紹了kNN算法python實(shí)現(xiàn)和簡(jiǎn)單數(shù)字識(shí)別的方法,詳細(xì)講述了kNN算法的優(yōu)缺點(diǎn)及原理,并給出了應(yīng)用實(shí)例,需要的朋友可以參考下2014-11-11Python?HMAC模塊維護(hù)數(shù)據(jù)安全技術(shù)實(shí)例探索
本篇博客將帶領(lǐng)讀者深入探索Python中HMAC模塊的高級(jí)應(yīng)用,通過(guò)豐富的示例代碼和詳細(xì)的解釋,揭示HMAC在實(shí)際應(yīng)用場(chǎng)景中的多面光芒,從基礎(chǔ)概念到密碼存儲(chǔ)、文件完整性驗(yàn)證、API安全,再到與加密算法的巧妙結(jié)合2024-01-01Python搭建自己IP代理池的方法實(shí)現(xiàn)
本文主要介紹了Python搭建自己IP代理池的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02