python 函數(shù)進(jìn)階之閉包函數(shù)
閉包函數(shù)
什么是閉包函數(shù)
如果內(nèi)函數(shù)使用了外函數(shù)的局部變量,并且外函數(shù)把內(nèi)函數(shù)返回出來(lái)的過(guò)程叫做閉包,里面的內(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(): # 返回了同級(jí)內(nèi)函數(shù) x y return x, y # 外函數(shù)最終返回了 inner函數(shù) return inner
判斷是否是閉包函數(shù)
方法 | 作用 |
\__closure__ | 獲取閉包函數(shù)使用的局部變量 |
cell_contents | 獲取單元格對(duì)象當(dāng)中的閉包函數(shù) |
\__closure__
可以使用這個(gè)方法判斷一個(gè)函數(shù)是否是一個(gè)閉包函數(shù),因?yàn)殚]包函數(shù)必須要使用外函數(shù)的局部變量,如果返回None就說(shuō)明這個(gè)函數(shù)不是閉包函數(shù),如果返回的是一個(gè)元組,說(shuō)明這是一個(gè)閉包函數(shù),元組中有cell單元格對(duì)象,一個(gè)單元格對(duì)象表示這個(gè)閉包函數(shù)使用了幾個(gè)外函數(shù)的局部變量。
拿上述版本測(cè)試。
# 外函數(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(): # 返回了同級(jí)內(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__測(cè)試這個(gè)幾個(gè)函數(shù)是否是閉包函數(shù) print(outer.__closure__) print(func.__closure__) print(a.__closure__) print(b.__closure__) ''' 結(jié)果:除了外函數(shù)outer之外都返回了cell對(duì)象,說(shuō)明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單元格對(duì)象的形式展示的,我們并不能看出這個(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(): # 返回了同級(jí)內(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é)束之后就會(huì)被釋放掉。
如果內(nèi)函數(shù)使用了外函數(shù)的局部變量,那么這個(gè)變量就與閉包函數(shù)發(fā)生了綁定關(guān)系,就延長(zhǎng)該變量的生命周期。實(shí)際上就是內(nèi)存給它存儲(chǔ)了這個(gè)值,暫時(shí)不釋放。
下面的例子中,我們調(diào)用了函數(shù)outer并賦予了參數(shù)val的值為10,但是outer執(zhí)行完之后,outer的val并沒(méi)有被釋放,而是被閉包函數(shù)inner延長(zhǎng)了生命周期,所以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í)就會(huì)延長(zhǎng)val的生命周期,val就不會(huì)隨著outer的調(diào)用結(jié)束而被釋放
而是存儲(chǔ)在了內(nèi)存當(dāng)中,當(dāng)inner再次使用val時(shí),val就會(huì)將值賦予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ù)中的變量,并對(duì)閉包中的值起到了封裝包保護(hù)的作用,使外部無(wú)法訪問(wèn)。
我們做一個(gè)模擬鼠標(biāo)點(diǎn)擊的事件,可以看得出閉包函數(shù)封裝保護(hù)數(shù)據(jù)的作用。
現(xiàn)在只是一個(gè)普通的函數(shù),它無(wú)法對(duì)我們使用的變量的數(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ù)對(duì)數(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ù)對(duì)數(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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python爬蟲(chóng)實(shí)戰(zhàn)之用selenium爬取某旅游網(wǎng)站
上一篇我們已經(jīng)知道怎么簡(jiǎn)單使用selenium了,那么我們就從這篇博客來(lái)動(dòng)手爬取網(wǎng)站吧,文中有非常詳細(xì)的代碼示例,需要的朋友可以參考下2021-06-06python數(shù)據(jù)可視化自制職位分析生成崗位分析數(shù)據(jù)報(bào)表
之前網(wǎng)上也有不少關(guān)于行業(yè)的分析數(shù)據(jù),今天我們就根據(jù)不同崗位,公司類型規(guī)模,學(xué)歷要求,薪資分布等來(lái)進(jìn)行分析,把職位分析功能集合封裝起來(lái),做成一個(gè)小工具分享給大家吧2021-09-09Python 動(dòng)態(tài)導(dǎo)入對(duì)象,importlib.import_module()的使用方法
今天小編就為大家分享一篇Python 動(dòng)態(tài)導(dǎo)入對(duì)象,importlib.import_module()的使用方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-08-08python實(shí)現(xiàn)微信遠(yuǎn)程控制電腦
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)微信遠(yuǎn)程控制電腦的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02python爬蟲(chóng)之場(chǎng)內(nèi)ETF基金獲取
這篇文章主要介紹了python爬蟲(chóng)之場(chǎng)內(nèi)ETF基金獲取,ETF?是一種場(chǎng)內(nèi)交易型基金,可以在盤(pán)中進(jìn)行交易,交易性比場(chǎng)外基金強(qiáng)一點(diǎn),下文基于python的相關(guān)資料展開(kāi),需要的小伙伴可以參考一下2022-05-05python操作excel的包(openpyxl、xlsxwriter)
這篇文章主要為大家詳細(xì)介紹了python操作excel的包,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Python多線程threading和multiprocessing模塊實(shí)例解析
這篇文章主要介紹了Python多線程threading和multiprocessing模塊等相關(guān)內(nèi)容,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,這里分享給大家,需要的朋友可以參考下2018-01-01