久久国产乱子伦精品免费M,亚洲一区二区三区91,欧美国产在线视频,国产精品视频久久

Python with提前退出:坑與解決方案

問題的起源

早些時(shí)候使用with實(shí)現(xiàn)了一版全局進(jìn)程鎖,希望實(shí)現(xiàn)以下效果:

Python with提前退出:坑與解決方案

全局進(jìn)程鎖本身不用多說,大部分都依靠外部的緩存來實(shí)現(xiàn)的,redis上用的是setnx,有時(shí)候根據(jù)需要加上緩存擊穿問題、隨機(jī)延后以防止對緩存本身造成壓力。

當(dāng)時(shí)同樣寫了單元測試來測試這段代碼的有效性:

Python with提前退出:坑與解決方案

看起來非常完美地通過了。

這樣的一個(gè)全局進(jìn)程鎖是通過__enter__方法拋出異常, __exit__方法中捕獲異常來實(shí)現(xiàn)的:

Python with提前退出:坑與解決方案

看起來還不錯(cuò),畢竟單元測試都過了。

但是,這樣的實(shí)現(xiàn)是有問題的:

原因在于__exit__ 的執(zhí)行不是包在__enter__ 之外的,因此__enter__拋出的異常,不會被__exit__捕獲。

上面的單元測試恰好通過,是因?yàn)槠渲杏袃蓚€(gè)with語句,外面的with 捕獲的其實(shí)是里面的__enter__ 拋出的異常

使用改進(jìn)后的單元測試:

Python with提前退出:坑與解決方案

就會發(fā)現(xiàn)單元測試過不去了。

這個(gè)問題是我試圖使用with實(shí)現(xiàn)另一個(gè)邏輯:AB測試 時(shí)出現(xiàn)的,同樣是__enter__拋出異常,__exit__ 試圖捕獲:

Python with提前退出:坑與解決方案

調(diào)試沒有通過的單元測試的時(shí)候發(fā)現(xiàn),拋出異常后根本沒有執(zhí)行到__enter__。

第一種解決方案

既然想明白了with的執(zhí)行順序,那么第一種解決方案就呼之欲出了:既然__exit__捕獲的異常在__enter__執(zhí)行完成之后,那么我們提供一個(gè)函數(shù)確認(rèn)一下就可以了,把ABContext實(shí)現(xiàn)改成這樣:

Python with提前退出:坑與解決方案

使用的時(shí)候:

Python with提前退出:坑與解決方案

但這樣的解決方法并不優(yōu)雅,萬一使用這個(gè)ABContext的時(shí)候忘記用ensure方法了,那么就等于完全沒用這個(gè)Context方法,太容易失誤了,而且代碼也失去了Pythonic的性質(zhì)。

第二種解決方法

翻了一下contextlib的標(biāo)準(zhǔn)庫文檔,發(fā)現(xiàn)有一個(gè)已經(jīng)廢棄的函數(shù):contextlib.nested

Python with提前退出:坑與解決方案

可以執(zhí)行多個(gè)上下文:

Python with提前退出:坑與解決方案

這個(gè)廢棄的特性在Python2.7之后,可以直接由with關(guān)鍵字執(zhí)行,形如:

Python with提前退出:坑與解決方案

這個(gè)特性還不錯(cuò),根據(jù)__enter__的執(zhí)行順序的話,那么我們可以實(shí)現(xiàn)一個(gè)由第一個(gè) context的__exit__來捕獲,第二個(gè)context的__enter__來拋出異常,

如同這樣:

Python with提前退出:坑與解決方案

結(jié)合前面我們實(shí)現(xiàn)的ABContext的使用是這樣的:

Python with提前退出:坑與解決方案

good,單元測試就這樣過了!

能不能再給力點(diǎn)?

確實(shí),在with里要寫倆context有點(diǎn)蛋疼,并不是特別優(yōu)雅,能不能還是回到最初的那種用法:我們只用寫一條context,這一個(gè)context做到了兩個(gè)context的事情?

要是nested那個(gè)函數(shù)還在就好了。。要的其實(shí)就是它的功能。

Python3.1之后contextlib提供了一個(gè)ExitStack的功能來提供一個(gè)模擬的功能,但試了一下發(fā)現(xiàn),實(shí)際上只調(diào)用了__enter__方法,但沒有做對應(yīng)的異常捕獲。

第三種解決方案

哈哈哈哈把自己繞到圈子里去了,想了一下,同樣是一個(gè)縮進(jìn)的代碼塊,為什么不能用if來解決呢!不就是個(gè):

Python with提前退出:坑與解決方案

的問題。。。

TIL

總之學(xué)到了contextlib里的一些有用的函數(shù)和裝飾器,也第一次發(fā)現(xiàn)with可以放個(gè)context。

雖然放多個(gè)context的動態(tài)構(gòu)造還有待研究,with 后面的代碼塊也不能填一個(gè)元組或者列表。。惆悵。。

好啦!今天的分享到這里就結(jié)束了,希望大家持續(xù)關(guān)注馬哥教育官網(wǎng),每天都會有大量優(yōu)質(zhì)內(nèi)容與大家分享!聲明:文章轉(zhuǎn)載于網(wǎng)絡(luò),版權(quán)歸原作者所有,如有侵權(quán),請及時(shí)聯(lián)系!

 

相關(guān)新聞

歷經(jīng)多年發(fā)展,已成為國內(nèi)好評如潮的Linux云計(jì)算運(yùn)維、SRE、Devops、網(wǎng)絡(luò)安全、云原生、Go、Python開發(fā)專業(yè)人才培訓(xùn)機(jī)構(gòu)!

    1. 主站蜘蛛池模板: 班玛县| 景宁| 缙云县| 太和县| 鹿邑县| 徐汇区| 无为县| 福建省| 漳州市| 蚌埠市| 四会市| 张家口市| 临城县| 神木县| 邵东县| 航空| 儋州市| 应城市| 望奎县| 佛学| 锡林郭勒盟| 宁强县| 麦盖提县| 凤台县| 安仁县| 三江| 叶城县| 高平市| 梨树县| 平凉市| 和林格尔县| 炎陵县| 芦溪县| 襄垣县| 金昌市| 内乡县| 黄梅县| 揭东县| 平乐县| 保山市| 永善县|