網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
1、閉包的概念
請(qǐng)大家跟我理解一下,如果在一個(gè)函數(shù)的內(nèi)部定義了另一個(gè)函數(shù),外部的我們叫他外函數(shù),內(nèi)部的我們叫他內(nèi)函數(shù)。閉包: 在一個(gè)外函數(shù)中定義了一個(gè)內(nèi)函數(shù),內(nèi)函數(shù)里運(yùn)用了外函數(shù)的臨時(shí)變量,并且外函數(shù)的返回值是內(nèi)函數(shù)的引用。這樣就構(gòu)成了一個(gè)閉包。一般情況下,在我們認(rèn)知當(dāng)中,如果一個(gè)函數(shù)結(jié)束,函數(shù)的內(nèi)部所有東西都會(huì)釋放掉,還給內(nèi)存,局部變量都會(huì)消失。但是閉包是一種特殊情況,如果外函數(shù)在結(jié)束的時(shí)候發(fā)現(xiàn)有自己的臨時(shí)變量將來(lái)會(huì)在內(nèi)部函數(shù)中用到,就把這個(gè)臨時(shí)變量綁定給了內(nèi)部函數(shù),然后自己再結(jié)束。
2、實(shí)現(xiàn)一個(gè)閉包
# 將函數(shù)作為返回值返回,也是一種高階函數(shù) # 這種高階函數(shù)我們也稱(chēng)為叫做閉包,通過(guò)閉包可以創(chuàng)建一些只有當(dāng)前函數(shù)能訪(fǎng)問(wèn)的變量 # 可以將一些私有的數(shù)據(jù)藏到的閉包中 def outer(): a = 10 # 函數(shù)內(nèi)部再定義一個(gè)函數(shù) def inner(): print('我是outer', a) # 將內(nèi)部函數(shù) inner作為返回值返回 return inner # r是一個(gè)函數(shù)對(duì)象,是調(diào)用fn()后返回的函數(shù)對(duì)象 # 這個(gè)函數(shù)實(shí)在fn()內(nèi)部定義,并不是全局函數(shù) # 所以這個(gè)函數(shù)總是能訪(fǎng)問(wèn)到fn()函數(shù)內(nèi)的變量 # 外函數(shù)返回了內(nèi)函數(shù)的引用 fn = outer() # r()相當(dāng)于調(diào)用了inner()函數(shù) print("outer引用地址:", outer) print("inner引用地址:", fn) fn() """ 輸出結(jié)果: outer引用地址: <function outer at 0x0000000002BB5948> inner引用地址: <function outer.<locals>.inner at 0x0000000002BB58B8> 我是outer 10 """
說(shuō)明上述代碼:
對(duì)于閉包,在外函數(shù)outer
中 最后return inner,我們?cè)谡{(diào)用外函數(shù) fn = outer()
的時(shí)候,outer
函數(shù)返回了inner
函數(shù)對(duì)象,inner
函數(shù)對(duì)象是一個(gè)函數(shù)的引用,這個(gè)引用被存入了fn
對(duì)象中。所以接下來(lái)我們?cè)龠M(jìn)行fn()
的時(shí)候,相當(dāng)于運(yùn)行了inner
函數(shù)。
提示:
一個(gè)函數(shù),如果函數(shù)名后緊跟一對(duì)括號(hào),相當(dāng)于調(diào)用這個(gè)函數(shù)。如果不跟括號(hào),相當(dāng)于只是一個(gè)函數(shù)的名字,里面存了函數(shù)所在位置的引用。
3、在閉包中外函數(shù)把臨時(shí)變量綁定給內(nèi)函數(shù)
按照我們正常的認(rèn)知,一個(gè)函數(shù)結(jié)束的時(shí)候,會(huì)把自己的臨時(shí)變量都釋放還給內(nèi)存,之后變量都不存在了。一般情況下,確實(shí)是這樣的。但是閉包是一個(gè)特別的情況。外部函數(shù)發(fā)現(xiàn),自己的臨時(shí)變量會(huì)在將來(lái)的內(nèi)部函數(shù)中用到,自己在結(jié)束的時(shí)候,返回內(nèi)函數(shù)的同時(shí),會(huì)把外函數(shù)的臨時(shí)變量送給內(nèi)函數(shù)綁定在一起。所以外函數(shù)已經(jīng)結(jié)束了,調(diào)用內(nèi)函數(shù)的時(shí)候仍然能夠使用外函數(shù)的臨時(shí)變量。
在我編寫(xiě)的實(shí)例中,我兩次調(diào)用外部函數(shù)outer
,分別傳入的值是10和20。內(nèi)部函數(shù)只定義了一次,我們發(fā)現(xiàn)調(diào)用的時(shí)候,內(nèi)部函數(shù)是能識(shí)別外函數(shù)的臨時(shí)變量是不一樣的。
Python中一切都是對(duì)象,雖然函數(shù)我們只定義了一次,但是外函數(shù)在運(yùn)行的時(shí)候,實(shí)際上是按照里面代碼執(zhí)行的,外函數(shù)里創(chuàng)建了一個(gè)函數(shù),我們每次調(diào)用外函數(shù),它都創(chuàng)建一個(gè)內(nèi)函數(shù),雖然代碼一樣,但是卻創(chuàng)建了不同的對(duì)象,并且把每次傳入的臨時(shí)變量數(shù)值綁定給內(nèi)函數(shù),再把內(nèi)函數(shù)引用返回。
所以我們每次調(diào)用外函數(shù),都返回不同的實(shí)例對(duì)象的引用,他們的功能是一樣的,但是它們實(shí)際上不是同一個(gè)函數(shù)對(duì)象。
下面示例進(jìn)行演示:
def outer(num): a = num # 函數(shù)內(nèi)部再定義一個(gè)函數(shù) def inner(): print('我是outer', a) # 將內(nèi)部函數(shù) inner作為返回值返回 return inner fn1 = outer(10) fn2 = outer(20) print("inner引用地址:", fn1) fn1() print("inner引用地址:", fn2) fn2() """ 輸出結(jié)果: inner引用地址: <function outer.<locals>.inner at 0x00000000026B58B8> 我是outer 10 inner引用地址: <function outer.<locals>.inner at 0x00000000026B5828> 我是outer 20 """ # 注意兩個(gè)inner的地址不一樣,一個(gè)是8B8,一個(gè)是828。
4、閉包中內(nèi)函數(shù)修改外函數(shù)局部變量
在基本的Python語(yǔ)法當(dāng)中,一個(gè)函數(shù)可以隨意讀取全局?jǐn)?shù)據(jù),但是要修改全局?jǐn)?shù)據(jù)的時(shí)候有兩種方法
global
聲明全局變量。全局變量是可變類(lèi)型數(shù)據(jù)的時(shí)候可以修改。
在閉包內(nèi)函數(shù)也是類(lèi)似的情況。在內(nèi)函數(shù)中想修改閉包變量(外函數(shù)綁定給內(nèi)函數(shù)的局部變量)的時(shí)候,在Python3中,可以用nonlocal
關(guān)鍵字聲明一個(gè)變量, 表示這個(gè)變量不是局部變量空間的變量,需要向上一層變量空間找這個(gè)變量。
示例:
def outer(num): a = num b = 10 # a和b都是閉包變量 print("原始a值為", a) # inner內(nèi)函數(shù) def inner(): # 內(nèi)函數(shù)中想修改閉包變量 # nonlocal關(guān)鍵字聲明變量 nonlocal a a += b print('我是outer的a', a) # 將內(nèi)部函數(shù) inner作為返回值返回 return inner fn1 = outer(10) fn1() """ 輸出結(jié)果: 原始a值為 10 我是outer的a 20 """
在內(nèi)函數(shù)中,對(duì)閉包變量進(jìn)行了修改,打印出來(lái)的結(jié)果也確實(shí)是修改之后的結(jié)果。
5、注意:
還有一點(diǎn)需要注意,閉包變量實(shí)際上只有一份,每次調(diào)用一份閉包變量。(這個(gè)在Python實(shí)現(xiàn)的單利模式下來(lái)解釋更多)
def outer(num): a = num b = 10 # a和b都是閉包變量 print("原始a值為", a) # inner內(nèi)函數(shù) def inner(): # 內(nèi)函數(shù)中想修改閉包變量 # nonlocal關(guān)鍵字聲明變量 nonlocal a a += b print('我是outer的a', a) # 將內(nèi)部函數(shù) inner作為返回值返回 return inner fn1 = outer(10) fn1() fn1() fn1() """ 輸出結(jié)果: 原始a值為 10 我是outer的a 20 我是outer的a 30 我是outer的a 40 """
可以看到第二次第二次調(diào)用fn1()
方法,a
的值有增加了10。
6、練習(xí):
# 求多個(gè)數(shù)的平均值 # nums = [50,30,20,10,77] # sum()是一個(gè)求和函數(shù) # sum()用來(lái)求一個(gè)列表中所有元素的和 # print(sum(nums)/len(nums)) # 結(jié)果:37.4 # 形成閉包的要件 # ① 函數(shù)嵌套 # ② 將內(nèi)部函數(shù)作為返回值返回 # ③ 內(nèi)部函數(shù)必須要使用到外部函數(shù)的變量 def make_averager(): # 創(chuàng)建一個(gè)列表,用來(lái)保存數(shù)值 nums = [] # 創(chuàng)建一個(gè)函數(shù),用來(lái)計(jì)算平均值 def averager(n) : # 將n添加到列表中 nums.append(n) # 求平均值 return sum(nums)/len(nums) return averager # 創(chuàng)建對(duì)象,現(xiàn)在就是獲得了內(nèi)函數(shù)對(duì)象的引用 averager = make_averager() # 調(diào)用內(nèi)涵書(shū)對(duì)象 # 這里注意,雖然是調(diào)用外函數(shù)創(chuàng)建的對(duì)象, # 但是獲得的是內(nèi)函數(shù)對(duì)象的引用,內(nèi)函數(shù)是有形參的, # 所以averager對(duì)象相當(dāng)于是內(nèi)函數(shù)對(duì)象。 # 所以調(diào)用內(nèi)函數(shù)就要傳遞形參。 print(averager(10)) print(averager(20)) print(averager(30)) print(averager(40))
總結(jié)
原文鏈接:https://www.cnblogs.com/liuyuelinfighting/p/15840400.html
相關(guān)推薦
- 2023-04-17 Python屬性私有化詳解_python
- 2023-02-02 C語(yǔ)言中的冒泡排序問(wèn)題_C 語(yǔ)言
- 2022-06-07 教你使用Jenkins集成Harbor自動(dòng)發(fā)布鏡像_docker
- 2022-07-12 Shell中常用的基礎(chǔ)命令
- 2022-07-02 Python零錢(qián)兌換的實(shí)現(xiàn)代碼_python
- 2022-08-02 使用shell讀取ini文件方法步驟_linux shell
- 2022-06-14 C#實(shí)現(xiàn)加密的幾種方法介紹_C#教程
- 2022-09-08 go語(yǔ)言中的協(xié)程詳解_Golang
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支