python try...finally...的實(shí)現(xiàn)方法
1. 關(guān)于 try.. finally..
假如上帝用 python 為每一個(gè)來(lái)到世界的生物編寫(xiě)程序,那么除去中間過(guò)程的種種復(fù)雜實(shí)現(xiàn),最不可避免的就是要保證每個(gè)實(shí)例最后都要掛掉。代碼可簡(jiǎn)寫(xiě)如下:
try: born() # 出生 # 正常降臨世界 # do something.. except ValueError: # 安排錯(cuò)誤 # do something... except AttributeError: # 特征錯(cuò)誤 # do something... except TypeError: # 種類(lèi)錯(cuò)誤 # do something... ... # 等等雜七雜八的錯(cuò)誤 finally: go_die() # 掛掉 come_to_see_me() # 然后來(lái)見(jiàn)我 reincarnate() # 下一輪,安排!
這就是 finally
的作用和實(shí)例。就算捕獲異常后再次出現(xiàn)異常,最終也能保證 go_die
方法會(huì)執(zhí)行,但是,如果 go_die
方法出現(xiàn)錯(cuò)誤,那么就不能正常去見(jiàn)上帝了。為了保證每個(gè)生物(不管有沒(méi)有掛掉)都能見(jiàn)到上帝他老人家,并開(kāi)始下一個(gè)輪回(不管有沒(méi)有見(jiàn)到),需要做如下處理:
... finally: try: go_die() finally: try: come_to_see_me() finally: reincarnate()
OK,功能雖然實(shí)現(xiàn)了,但按照 The Zen of Python
所說(shuō):Flat is better than nested.(扁平優(yōu)于嵌套),那么這段代碼就略顯丑陋了。為了遵循 python 美學(xué),我們可以對(duì)這段進(jìn)行優(yōu)化,使它看起來(lái)更為美觀。
2. 錯(cuò)誤的上下文:__context__
在此之前,需要引入一個(gè)新的概念: __context__
,__context__
的字面意思就是上下文,它屬于錯(cuò)誤的一個(gè)屬性。在錯(cuò)誤捕獲中,它意味著當(dāng)你處理一個(gè)錯(cuò)誤時(shí),另一個(gè)錯(cuò)誤發(fā)生了。也就是說(shuō),你所捕獲的錯(cuò)誤雖然被成功捕獲了,但當(dāng)捕獲完成時(shí),你的一些操作導(dǎo)致另一個(gè)錯(cuò)誤發(fā)生,而這個(gè)錯(cuò)誤并沒(méi)有被捕獲。通常情況下,如果處理的好,那么當(dāng)前錯(cuò)誤的 __context__
的值為 None
,如果處理不好那就是你所捕獲的錯(cuò)誤。比如下面的代碼:
def type_err(): raise TypeError('this is a type error.') def after_type_err(): raise ValueError('this is a value error.') try: type_err() except TypeError: after_type_err()
執(zhí)行結(jié)果為:
Traceback (most recent call last): File "<ipython-input-4-189a22d65266>", line 8, in <module> type_err() File "<ipython-input-4-189a22d65266>", line 2, in type_err raise TypeError('this is a type error.') TypeError: this is a type error. During handling of the above exception, another exception occurred: Traceback (most recent call last): File "C:\Users\lineu\AppData\Local\Programs\Python\Python37\lib\site-packages\IPython\core\interactiveshell.py", line 3326, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "<ipython-input-4-189a22d65266>", line 10, in <module> after_type_err() File "<ipython-input-4-189a22d65266>", line 5, in after_type_err raise ValueError('this is a value error.') ValueError: this is a value error.
在上面的錯(cuò)誤信息中,當(dāng)前錯(cuò)誤類(lèi)型為 ValueError
,它的 __context__
屬性值為 TypeError
實(shí)例,而 TypeError 實(shí)例的 __context__
為 None
。
3. FinalExecutor:優(yōu)雅的 finally
有了 __context__
的概念,我們就可以基于此實(shí)現(xiàn)一個(gè)優(yōu)雅的“輪回”了?;舅悸窞椋阂来螆?zhí)行方法,如果方法報(bào)錯(cuò),那么就將該錯(cuò)誤的 __context__
值設(shè)置為上一個(gè)錯(cuò)誤(如果有)。最后等到所有方法執(zhí)行完畢,再拋出最后一個(gè)錯(cuò)誤,那么此時(shí)的錯(cuò)誤將包含所有可能被引發(fā)的錯(cuò)誤信息。具體代碼如下:
class FinalExecutor(object): """終極執(zhí)行器 用于確保你所有的方法都會(huì)被執(zhí)行(不管中途有沒(méi)有方法報(bào)錯(cuò)) 同時(shí)能看到正確的錯(cuò)誤信息 """ def __init__(self): self.last_err = None # 保存最近發(fā)生的錯(cuò)誤 def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): # 如果有發(fā)生錯(cuò)誤,則拋出 if self.last_err: raise self.last_err def call(self, func, *args, **kwargs): """調(diào)用執(zhí)行方法""" try: func(*args, **kwargs) except Exception as e: # Exception 捕獲所有繼承自它或它子類(lèi)的錯(cuò)誤類(lèi)型 # 捕獲它等于捕獲幾乎所有錯(cuò)誤 if self.last_err: # 將本次錯(cuò)誤的上下文定義為上一次錯(cuò)誤 e.__context__ = self.last_err # 更新為當(dāng)前錯(cuò)誤 self.last_err = e
我們的終極執(zhí)行器使用示例為:
# 定義 3 個(gè)方法用于測(cè)試 def type_err(): print('type error') raise TypeError('x') def value_err(): print('value error') raise ValueError('x') def attr_err(): print('attr error') raise AttributeError('x') # 使用 with 語(yǔ)句來(lái)啟動(dòng)終極執(zhí)行器 with FinalExecutor() as e: e.call(type_err) e.call(value_err) e.call(attr_err)
運(yùn)行可以看到方法最終都被執(zhí)行了,且錯(cuò)誤信息一個(gè)不漏:
type error value error attr error Traceback (most recent call last): File "<ipython-input-5-1b07c576630b>", line 19, in call func(*args, **kwargs) File "<ipython-input-6-d602d89ed0e7>", line 3, in type_err raise TypeError('x') TypeError: x During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<ipython-input-5-1b07c576630b>", line 19, in call func(*args, **kwargs) File "<ipython-input-6-d602d89ed0e7>", line 8, in value_err raise ValueError('x') ValueError: x During handling of the above exception, another exception occurred: Traceback (most recent call last): File "C:\Users\lineu\AppData\Local\Programs\Python\Python37\lib\site-packages\IPython\core\interactiveshell.py", line 3326, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "<ipython-input-6-d602d89ed0e7>", line 19, in <module> e.call(attr_err) File "<ipython-input-5-1b07c576630b>", line 15, in __exit__ raise self.last_err File "<ipython-input-5-1b07c576630b>", line 19, in call func(*args, **kwargs) File "<ipython-input-6-d602d89ed0e7>", line 13, in attr_err raise AttributeError('x') AttributeError: x
4. 使用 ExitStack
有了我們的終極執(zhí)行器,上帝就可以優(yōu)雅的寫(xiě)代碼了。為了讓每個(gè)人都能這樣優(yōu)雅的寫(xiě) python 代碼,python 為我們提供了一個(gè)封裝好的功能,當(dāng)然它的實(shí)現(xiàn)要比我們的終極執(zhí)行器復(fù)雜一些(考慮的也更周到一些~)。我們可以通過(guò) contextlib
模塊導(dǎo)入該方法并使用:
from contextlib import ExitStack with ExitStack() as stack: stack.callback(type_err) stack.callback(value_err) stack.callback(attr_err)
注意該 ExitStack
與 FinalExecutor
不同的是,它是倒序執(zhí)行的。
以上就是python try...finally...的實(shí)現(xiàn)方法的詳細(xì)內(nèi)容,更多關(guān)于python try...finally的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python面向?qū)ο缶幊讨?lèi)的進(jìn)階
這篇文章主要介紹了Python面向?qū)ο缶幊讨?lèi)的引用,引用 Reference是對(duì)象的指針,引用是內(nèi)存中真實(shí)對(duì)象的指針,表示為變量名或者內(nèi)存地址、每個(gè)對(duì)象存在至少一個(gè)引用,id()函數(shù)用于獲得引用,想具體了解的小伙伴可以參考下面文章的內(nèi)容2021-11-11Python 分析Nginx訪問(wèn)日志并保存到MySQL數(shù)據(jù)庫(kù)實(shí)例
這篇文章主要介紹了Python 分析Nginx訪問(wèn)日志并保存到MySQL數(shù)據(jù)庫(kù)實(shí)例,需要的朋友可以參考下2014-03-03Pycharm中import torch報(bào)錯(cuò)的快速解決方法
這篇文章主要介紹了Pycharm中import torch報(bào)錯(cuò)的快速解決方法,很多朋友容易碰到這個(gè)問(wèn)題,今天小編特此把解決方案分享到腳本之家平臺(tái)供大家參考,需要的朋友可以參考下2020-03-03Python+Django搭建自己的blog網(wǎng)站
一直有種想要搭建自己博客系統(tǒng)打沖動(dòng),可惜需要前端知識(shí)較多,而且還要安裝一系列軟件并配置(windows平臺(tái)),后來(lái)偶然發(fā)現(xiàn)了Django這個(gè)神器,恰巧剛學(xué)過(guò)python,于是就又裝了ubuntu(安裝各種軟件配置都方便),折騰了半天,終于搭建起來(lái)自己的第一個(gè)簡(jiǎn)易博客。2018-03-03Python使用窮舉法求兩個(gè)數(shù)的最大公約數(shù)問(wèn)題
這篇文章主要介紹了Python使用窮舉法求兩個(gè)數(shù)的最大公約數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12python人工智能tensorflow函數(shù)np.random模塊使用
這篇文章主要為大家介紹了python人工智能tensorflow函數(shù)np.random模塊使用方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Python 內(nèi)置函數(shù)memoryview(obj)的具體用法
本篇文章主要介紹了Python 內(nèi)置函數(shù)memoryview(obj)的具體用法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11