python try...finally...的實現(xiàn)方法
1. 關(guān)于 try.. finally..
假如上帝用 python 為每一個來到世界的生物編寫程序,那么除去中間過程的種種復雜實現(xiàn),最不可避免的就是要保證每個實例最后都要掛掉。代碼可簡寫如下:
try: born() # 出生 # 正常降臨世界 # do something.. except ValueError: # 安排錯誤 # do something... except AttributeError: # 特征錯誤 # do something... except TypeError: # 種類錯誤 # do something... ... # 等等雜七雜八的錯誤 finally: go_die() # 掛掉 come_to_see_me() # 然后來見我 reincarnate() # 下一輪,安排!
這就是 finally 的作用和實例。就算捕獲異常后再次出現(xiàn)異常,最終也能保證 go_die 方法會執(zhí)行,但是,如果 go_die 方法出現(xiàn)錯誤,那么就不能正常去見上帝了。為了保證每個生物(不管有沒有掛掉)都能見到上帝他老人家,并開始下一個輪回(不管有沒有見到),需要做如下處理:
...
finally:
try:
go_die()
finally:
try:
come_to_see_me()
finally:
reincarnate()
OK,功能雖然實現(xiàn)了,但按照 The Zen of Python 所說:Flat is better than nested.(扁平優(yōu)于嵌套),那么這段代碼就略顯丑陋了。為了遵循 python 美學,我們可以對這段進行優(yōu)化,使它看起來更為美觀。
2. 錯誤的上下文:__context__
在此之前,需要引入一個新的概念: __context__,__context__ 的字面意思就是上下文,它屬于錯誤的一個屬性。在錯誤捕獲中,它意味著當你處理一個錯誤時,另一個錯誤發(fā)生了。也就是說,你所捕獲的錯誤雖然被成功捕獲了,但當捕獲完成時,你的一些操作導致另一個錯誤發(fā)生,而這個錯誤并沒有被捕獲。通常情況下,如果處理的好,那么當前錯誤的 __context__ 的值為 None,如果處理不好那就是你所捕獲的錯誤。比如下面的代碼:
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.
在上面的錯誤信息中,當前錯誤類型為 ValueError,它的 __context__ 屬性值為 TypeError 實例,而 TypeError 實例的 __context__ 為 None。
3. FinalExecutor:優(yōu)雅的 finally
有了 __context__ 的概念,我們就可以基于此實現(xiàn)一個優(yōu)雅的“輪回”了。基本思路為:依次執(zhí)行方法,如果方法報錯,那么就將該錯誤的 __context__ 值設(shè)置為上一個錯誤(如果有)。最后等到所有方法執(zhí)行完畢,再拋出最后一個錯誤,那么此時的錯誤將包含所有可能被引發(fā)的錯誤信息。具體代碼如下:
class FinalExecutor(object):
"""終極執(zhí)行器
用于確保你所有的方法都會被執(zhí)行(不管中途有沒有方法報錯)
同時能看到正確的錯誤信息
"""
def __init__(self):
self.last_err = None # 保存最近發(fā)生的錯誤
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 如果有發(fā)生錯誤,則拋出
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 捕獲所有繼承自它或它子類的錯誤類型
# 捕獲它等于捕獲幾乎所有錯誤
if self.last_err:
# 將本次錯誤的上下文定義為上一次錯誤
e.__context__ = self.last_err
# 更新為當前錯誤
self.last_err = e
我們的終極執(zhí)行器使用示例為:
# 定義 3 個方法用于測試
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 語句來啟動終極執(zhí)行器
with FinalExecutor() as e:
e.call(type_err)
e.call(value_err)
e.call(attr_err)
運行可以看到方法最終都被執(zhí)行了,且錯誤信息一個不漏:
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)雅的寫代碼了。為了讓每個人都能這樣優(yōu)雅的寫 python 代碼,python 為我們提供了一個封裝好的功能,當然它的實現(xiàn)要比我們的終極執(zhí)行器復雜一些(考慮的也更周到一些~)。我們可以通過 contextlib 模塊導入該方法并使用:
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...的實現(xiàn)方法的詳細內(nèi)容,更多關(guān)于python try...finally的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python使用signal定時結(jié)束AsyncIOScheduler任務(wù)的問題
這篇文章主要介紹了Python使用signal定時結(jié)束AsyncIOScheduler任務(wù),在使用aiohttp結(jié)合apscheduler的AsyncIOScheduler模擬定點并發(fā)的時候遇到兩個問題,針對每個問題給大家詳細介紹,需要的朋友可以參考下2021-07-07
Python實現(xiàn)隨機生成算術(shù)題的示例代碼
這篇文章主要為大家詳細介紹了如何利用Python實現(xiàn)隨機生成算術(shù)題的功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2023-04-04
使用Pandas進行時間序列分析的10個關(guān)鍵點詳解
這篇文章主要為大家詳細介紹了使用Pandas進行時間序列分析的10個關(guān)鍵點,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-10-10
Python基于gevent實現(xiàn)文件字符串查找器
這篇文章主要介紹了Python基于gevent實現(xiàn)文件字符串查找器,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
為2021年的第一場雪錦上添花:用matplotlib繪制雪花和雪景
這篇文章主要介紹了為2021年的第一場雪錦上添花:用matplotlib繪制雪花和雪景,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-01-01

