python 函數(shù)進(jìn)階之閉包函數(shù)
閉包函數(shù)
什么是閉包函數(shù)
如果內(nèi)函數(shù)使用了外函數(shù)的局部變量,并且外函數(shù)把內(nèi)函數(shù)返回出來的過程叫做閉包,里面的內(nèi)函數(shù)是閉包函數(shù)。
# 外函數(shù) outer
def outer():
# 外函數(shù)變量 num
var = '外函數(shù)局部變量'
# 內(nèi)函數(shù) inner
def inner():
# 內(nèi)函數(shù)使用了外函數(shù)的變量 num
print('內(nèi)函數(shù)使用了:' + var)
# 外函數(shù)將使用了外函數(shù)的局部變量的內(nèi)函數(shù)返回
return inner
# 返回出的結(jié)果就是內(nèi)函數(shù) inner,現(xiàn)在inner就是一個(gè)閉包函數(shù)
func = outer()
# 執(zhí)行返回出的 inner 函數(shù)
func() # 內(nèi)函數(shù)使用了:外函數(shù)局部變量下面是一個(gè)復(fù)雜的版本。
inner函數(shù)返回了函數(shù)x 和 y,x 和 y是外函數(shù)的內(nèi)函數(shù),雖然覆蓋了原有的外函數(shù)的局部變量,但是這兩個(gè)函數(shù)本質(zhì)上還是外函數(shù)的布局變量,所以外函數(shù)返回了inner,inner就是一個(gè)閉包函數(shù)。
inner返回了外函數(shù)的x和y函數(shù),x和y函數(shù)都是用了外函數(shù)的內(nèi)函數(shù)num3,外函數(shù)返回inner,inner返回了x和y,所以變相的就是外函數(shù)返回了x和y,所以x和y也是閉包函數(shù)。
# 外函數(shù) def outer(): # 外函數(shù)的局部變量 x = 1 y = 2 num3 = 3 # 內(nèi)函數(shù) x 重名變量 x def x(): # 調(diào)用修改了 變量 num3 nonlocal num3 num3 *= 10 print(num3) # 內(nèi)函數(shù) y 重名變量y def y(): # 調(diào)用修改了 變量num3 nonlocal num3 num3 += 10 print(num3) # 內(nèi)函數(shù)inner def inner(): # 返回了同級內(nèi)函數(shù) x y return x, y # 外函數(shù)最終返回了 inner函數(shù) return inner
判斷是否是閉包函數(shù)
方法 | 作用 |
\__closure__ | 獲取閉包函數(shù)使用的局部變量 |
cell_contents | 獲取單元格對象當(dāng)中的閉包函數(shù) |
\__closure__
可以使用這個(gè)方法判斷一個(gè)函數(shù)是否是一個(gè)閉包函數(shù),因?yàn)殚]包函數(shù)必須要使用外函數(shù)的局部變量,如果返回None就說明這個(gè)函數(shù)不是閉包函數(shù),如果返回的是一個(gè)元組,說明這是一個(gè)閉包函數(shù),元組中有cell單元格對象,一個(gè)單元格對象表示這個(gè)閉包函數(shù)使用了幾個(gè)外函數(shù)的局部變量。
拿上述版本測試。
# 外函數(shù) def outer(): # 外函數(shù)的局部變量 x = 1 y = 2 num3 = 3 # 內(nèi)函數(shù) x 重名變量 x def x(): # 調(diào)用修改了 變量 num3 nonlocal num3 num3 *= 10 print(num3) # 內(nèi)函數(shù) y 重名變量y def y(): # 調(diào)用修改了 變量num3 nonlocal num3 num3 += 10 print(num3) # 內(nèi)函數(shù)inner def inner(): # 返回了同級內(nèi)函數(shù) x y return x, y # 外函數(shù)最終返回了 inner函數(shù) return inner # 執(zhí)行outer返回的結(jié)果是inner func = outer() # func == inner # 執(zhí)行func返回的是 x y 函數(shù) a, b = func() # 使用__closure__測試這個(gè)幾個(gè)函數(shù)是否是閉包函數(shù) print(outer.__closure__) print(func.__closure__) print(a.__closure__) print(b.__closure__) ''' 結(jié)果:除了外函數(shù)outer之外都返回了cell對象,說明inner x y 都是閉包函數(shù) None (<cell at 0x0000022F246AECA8: function object at 0x0000022F2466C400>, <cell at 0x0000022F247F3558: function object at 0x0000022F24850730>) (<cell at 0x0000022F245D8708: int object at 0x00000000542280B0>,) (<cell at 0x0000022F245D8708: int object at 0x00000000542280B0>,) '''
cell_contents
雖然用??__closure__??獲取到了閉包函數(shù)使用的元素,但是是以cell單元格對象的形式展示的,我們并不能看出這個(gè)使用的 元素到底是什么東西,可以使用??cell_contents??查看。
# 外函數(shù) def outer(): # 外函數(shù)的局部變量 x = 1 y = 2 num3 = 3 # 內(nèi)函數(shù) x 重名變量 x def x(): # 調(diào)用修改了 變量 num3 nonlocal num3 num3 *= 10 print(num3) # 內(nèi)函數(shù) y 重名變量y def y(): # 調(diào)用修改了 變量num3 nonlocal num3 num3 += 10 print(num3) # 內(nèi)函數(shù)inner def inner(): # 返回了同級內(nèi)函數(shù) x y return x, y # 外函數(shù)最終返回了 inner函數(shù) return inner # 執(zhí)行outer返回的結(jié)果是inner func = outer() # func == inner # 使用__closure__返回了閉包函數(shù)使用的局部變量 tup = func.__closure__ # 使用 cell_contents 查看這些局部變量都是些什么 res = tup[0].cell_contents print(res) res = tup[1].cell_contents print(res) ''' 結(jié)果:可以看到inner 使用的局部變量使用外函數(shù)的內(nèi)函數(shù) x 和 y None <function outer.<locals>.x at 0x0000018D5A66C400> <function outer.<locals>.y at 0x0000018D5A850730> '''
閉包函數(shù)的特點(diǎn)
讓我們回憶一下,函數(shù)中創(chuàng)建的變量是一個(gè)什么變量?是一個(gè)局部變量。
局部變量的生命周期是多久?是等局部作用結(jié)束之后就會被釋放掉。
如果內(nèi)函數(shù)使用了外函數(shù)的局部變量,那么這個(gè)變量就與閉包函數(shù)發(fā)生了綁定關(guān)系,就延長該變量的生命周期。實(shí)際上就是內(nèi)存給它存儲了這個(gè)值,暫時(shí)不釋放。
下面的例子中,我們調(diào)用了函數(shù)outer并賦予了參數(shù)val的值為10,但是outer執(zhí)行完之后,outer的val并沒有被釋放,而是被閉包函數(shù)inner延長了生命周期,所以val可以一直在inner中按照調(diào)用outer函數(shù)的時(shí)候賦予的值10進(jìn)行運(yùn)算。
因?yàn)閮?nèi)函數(shù)inner使用了外函數(shù)outer的變量val,且outer返回了inner,所以inner是一個(gè)閉包函數(shù)。因?yàn)閕nner是一個(gè)閉包函數(shù),當(dāng)它調(diào)用outer的變量val時(shí)就會延長val的生命周期,val就不會隨著outer的調(diào)用結(jié)束而被釋放
而是存儲在了內(nèi)存當(dāng)中,當(dāng)inner再次使用val時(shí),val就會將值賦予inner。
def outer(val): def inner(num): return val + num return inner func = outer(10) res = func(10) print(res) # 20 res = func(20) print(res) # 30
閉包函數(shù)的意義
閉包可以優(yōu)先使用外函數(shù)中的變量,并對閉包中的值起到了封裝包保護(hù)的作用,使外部無法訪問。
我們做一個(gè)模擬鼠標(biāo)點(diǎn)擊的事件,可以看得出閉包函數(shù)封裝保護(hù)數(shù)據(jù)的作用。
現(xiàn)在只是一個(gè)普通的函數(shù),它無法對我們使用的變量的數(shù)據(jù)進(jìn)行保護(hù),在全局中這個(gè)數(shù)據(jù)可以被隨意的修改。
# 不使用閉包,當(dāng)函數(shù)中調(diào)用全局變量時(shí),外部也可以控制變量 # 全局變量 num = 0 # 點(diǎn)擊事件 def click_num(): # 每執(zhí)行一次數(shù)值 +1 global num num += 1 print(num) # 執(zhí)行點(diǎn)擊事件 click_num() # 1 click_num() # 2 click_num() # 3 # 在全局重新定義了num的值,num的值就被徹底的改變了,但是我們的程序的數(shù)據(jù)本不該如此。 num = 1231231 click_num() # 1231232 click_num() # 1231233 click_num() # 1231234
現(xiàn)在使用閉包函數(shù)對數(shù)據(jù)進(jìn)行封裝保護(hù),就不能在全局中隨意的修改我們使用的數(shù)據(jù)。
# 我們將需要使用的數(shù)據(jù)放在外函數(shù)中,點(diǎn)擊事件作為內(nèi)函數(shù)也放在外函數(shù)中,然后作為閉包返回。 def clickNum(): # 需要使用的數(shù)據(jù) num = 0 # 內(nèi)函數(shù)(真正執(zhí)行點(diǎn)擊事件的函數(shù)) def inner(): # 執(zhí)行點(diǎn)擊事件 nonlocal num num += 1 print(num) # 作為閉包返回 return inner # 返回閉包 click_num = clickNum() # # click_num == inner # 執(zhí)行點(diǎn)擊事件 click_num() # 1 click_num() # 2 click_num() # 3 # 全局中修改 num 的值 num = 123412341234 # 閉包函數(shù)對數(shù)據(jù)的封裝保護(hù)起到了作用 click_num() # 4 click_num() # 5 click_num() # 6
到此這篇關(guān)于python 函數(shù)進(jìn)階之閉包函數(shù)的文章就介紹到這了,更多相關(guān)python 閉包函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pandas和spark dataframe互相轉(zhuǎn)換實(shí)例詳解
這篇文章主要介紹了pandas和spark dataframe互相轉(zhuǎn)換實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
Python實(shí)現(xiàn)的微信好友數(shù)據(jù)分析功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)的微信好友數(shù)據(jù)分析功能,結(jié)合實(shí)例形式分析了Python使用itchat、pandas、pyecharts等模塊針對微信好友數(shù)據(jù)進(jìn)行統(tǒng)計(jì)與計(jì)算相關(guān)操作技巧,需要的朋友可以參考下2018-06-06
python 時(shí)間信息“2018-02-04 18:23:35“ 解析成字典形式的結(jié)果代碼詳解
本文是類方法給大家介紹如何將python 時(shí)間信息“2018-02-04 18:23:35“ 解析成字典形式的結(jié)果,需要的朋友可以參考下2018-04-04
Python數(shù)據(jù)類型之Number數(shù)字操作實(shí)例詳解
這篇文章主要介紹了Python數(shù)據(jù)類型之Number數(shù)字操作,結(jié)合實(shí)例形式詳細(xì)分析了Python數(shù)字類型的概念、功能、分類及常用數(shù)學(xué)函數(shù)相關(guān)使用技巧,需要的朋友可以參考下2019-05-05
python自定義函數(shù)def的應(yīng)用詳解
這篇文章主要介紹了python自定義函數(shù)def的應(yīng)用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
使用matplotlib庫實(shí)現(xiàn)圖形局部數(shù)據(jù)放大顯示的實(shí)踐
本文主要介紹了使用matplotlib庫實(shí)現(xiàn)圖形局部數(shù)據(jù)放大顯示的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02

