網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
1.什么是閉包
當(dāng)我們?cè)谕獠亢瘮?shù)中定義了一個(gè)內(nèi)部函數(shù),并且內(nèi)部函數(shù)能夠讀取到外部函數(shù)內(nèi)的變量,這種函數(shù)我們就稱為閉包。簡(jiǎn)單來(lái)說(shuō),閉包就是能夠讀取外部函數(shù)內(nèi)的變量的函數(shù)。
閉包的架子大概是這樣:
def demo_outer(x): def demo_inner(y): print("x的值:{}, y的值:{}, x + y 的值:{}".format(x, y, x + y)) return demo_inner do = demo_outer(12) do(34)
上面代碼執(zhí)行結(jié)果如下:
x的值:12, y的值:34, x + y 的值:46
上面的閉包代碼,和我們之前學(xué)習(xí)的裝飾器類似,我們?cè)谕獠亢瘮?shù) demo_outer
下定義了一個(gè)內(nèi)部函數(shù) demo_inner
,并且外部函數(shù)的返回值就是內(nèi)部函數(shù),同時(shí)在內(nèi)部函數(shù)中,我們引用到了外部函數(shù)的變量 x ,而閉包的作用就是可以將外層函數(shù)的變量保存在內(nèi)存中而不被銷毀。
2.閉包的實(shí)例
我們先準(zhǔn)備一個(gè)函數(shù)add(),每次調(diào)用該函數(shù)都只能傳一個(gè)數(shù) num ,每次返回的結(jié)果都是基于上一次結(jié)果的值 sum 進(jìn)行累加操作。例如,sum的默認(rèn)值為0,如果我們依次調(diào)用 add(10)、add(20)、add(30) 后,期望得到的最終結(jié)果是 sum = 60。
對(duì)于該問(wèn)題,因?yàn)樾枰诤瘮?shù)內(nèi)部對(duì)函數(shù)外部的變量進(jìn)行處理,我們可能會(huì)考慮使用 global 來(lái)處理。
sum = 0 def get_add_sum(num): global sum sum += num return sum print(get_add_sum(10)) # 輸出:10 print(get_add_sum(20)) # 輸出:30 print(get_add_sum(30)) # 輸出:60 print(sum) # 輸出:60
上面代碼中,我們?cè)诤瘮?shù)中通過(guò)全局變量 global 將 sum 聲明為全局變量,最終返回的結(jié)果也符合我們的期望。但因?yàn)槿肿兞刻`活了,不同模塊函數(shù)都能自由訪問(wèn)到全局變量,所以一般不推薦在函數(shù)內(nèi)部中定義全局變量。
對(duì)于上面的問(wèn)題,除了使用全局變量外,我們還可以通過(guò) 閉包 來(lái)實(shí)現(xiàn)。
sum = 0 def get_add_sum(sum): def add_num(num): nonlocal sum sum += num return sum return add_num add = get_add_sum(sum) print(add(10)) # 輸出:10 print(add(20)) # 輸出:30 print(add(30)) # 輸出:60 print(sum) # 輸出:0
在上面的閉包函數(shù)中,定義了外層函數(shù)和內(nèi)層嵌套函數(shù),執(zhí)行過(guò)程中,調(diào)用外層函數(shù) get_add_sum 返回的就是內(nèi)層嵌套函數(shù) add_num ,因?yàn)镻ython中函數(shù)也是對(duì)象,所以可以直接返回 add_num 。
而在內(nèi)層嵌套函數(shù)中則實(shí)現(xiàn)了我們想要的累加操作,在這里我們需使用關(guān)鍵字 nonlocal 來(lái)聲明外層函數(shù)中的變量,否則無(wú)法對(duì)外層函數(shù)中的變量進(jìn)行修改,其作用域只在閉包函數(shù)里面,所以當(dāng)我們?cè)谧詈蟠蛴?sum 最終結(jié)果,輸出的仍然是其初始值 0 。
我們?cè)賮?lái)看一個(gè)例子:
def outer(): res = [] for i in range(3): print("外部的i值:{}".format(i)) def inner(x): print("內(nèi)部的i值:{}".format(i)) return i + x res.append(inner) return res temp = outer() res = [i(10) for i in temp] print(res)
上面代碼的執(zhí)行結(jié)果如下:
外部的i值:0
外部的i值:1
外部的i值:2
內(nèi)部的i值:2
內(nèi)部的i值:2
內(nèi)部的i值:2
[12, 12, 12]
可以看到 res 的結(jié)果并不是 [10, 11, 12] ,因?yàn)?i 是外層函數(shù)的變量,并且其值是按 0,1,2 變化,因?yàn)樵摵瘮?shù)中無(wú)法保存外層函數(shù)的變量,故對(duì)于內(nèi)部函數(shù),其實(shí)際只能訪問(wèn)到最后外部變量的值 2 ,導(dǎo)致最終結(jié)果為:[12, 12, 12]。
在這里,我們可以使用閉包來(lái)保存函數(shù)的外部變量,修改代碼如下:
def outer(i): print("外部的i值:{}".format(i)) def inner(x): print("內(nèi)部的i值:{}".format(i)) return i + x return inner def demo(): res = [] for i in range(3): res.append(outer(i)) return res temp = demo() res = [i(10) for i in temp] print(res)
上面代碼的執(zhí)行結(jié)果如下:
外部的i值:0
外部的i值:1
外部的i值:2
內(nèi)部的i值:0
內(nèi)部的i值:1
內(nèi)部的i值:2
[10, 11, 12]
3.閉包和裝飾器的區(qū)別
在Python中,閉包傳遞的參數(shù)是變量,裝飾器傳遞的參數(shù)是函數(shù)對(duì)象,它們只是在傳參內(nèi)容上有不同。那么裝飾器是不是屬于閉包的一種呢,我們要怎么判斷一個(gè)函數(shù)是否是閉包呢?
我們可以打印 閉包 和 裝飾器 的屬性 __closure__
,如果一個(gè)函數(shù)是閉包,那么查看該屬性將會(huì)返回一個(gè)cell對(duì)象組成的tuple對(duì)象。
def demo_outer(x): """ 閉包 """ def demo_inner(y): print("x的值:{}, y的值:{}, x + y 的值:{}".format(x, y, x + y)) return demo_inner def demo_decorator(func): """ 裝飾器 """ def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @demo_decorator def method(): pass do = demo_outer(5) # 閉包 print("閉包的屬性:{}".format(do.__closure__)) dd = demo_decorator(method) # 裝飾器 print("裝飾器的屬性:{}".format(dd.__closure__))
執(zhí)行結(jié)果如下:
閉包的屬性:(<cell at 0x0000023F48C3C8E8: int object at 0x00007FF92017D4A0>,)
裝飾器的屬性:(<cell at 0x0000023F48C3C8B8: function object at 0x0000023F48CB97B8>,)
所以結(jié)合上面的結(jié)果,我們也可以這樣理解:裝飾器本質(zhì)上就是一個(gè)閉包函數(shù),它只是一個(gè)傳遞函數(shù)對(duì)象的閉包函數(shù)。
原文鏈接:https://blog.csdn.net/qdPython/article/details/126161036
相關(guān)推薦
- 2024-03-14 docker創(chuàng)建自定義網(wǎng)絡(luò)
- 2022-04-16 論文查重python文本相似性計(jì)算simhash源碼_python
- 2023-11-14 MathType 運(yùn)行時(shí)錯(cuò)誤‘53’:文件未找到:MathPage.WLL
- 2022-06-01 C語(yǔ)言詳細(xì)分析常見(jiàn)字符串函數(shù)與模擬實(shí)現(xiàn)_C 語(yǔ)言
- 2022-10-22 關(guān)于redis的延遲雙刪策略總結(jié)_Redis
- 2022-10-05 C#?獲取文件夾里所有文件名的詳細(xì)代碼_C#教程
- 2023-01-03 Oracle?CDB管理實(shí)現(xiàn)多租戶管理功能_oracle
- 2022-07-26 pyspark 中dataframe 按指定字段拆分為多列或者多行
- 最近更新
-
- 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概述快速入門
- 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)程分支