好域名做網(wǎng)站微信視頻號怎么推廣引流
Python 異常處理機制
- Python異常與異常處理機制
- 針對 Traceback 的解讀
- try-except-else-finally
- except語句
- except語句的機制
- 在 except 語句中引用當前被處理的 Python 異常
- finally語句
- finally語句執(zhí)行后才能拋出未被處理的異常
- finally中執(zhí)行return會導(dǎo)致異常丟失
- raise 語句
- raise 語句以及異常對象
- raise…from 語句
- Python 異常的基類 BaseException
Python異常與異常處理機制
Python使用稱為異常的特殊對象來管理程序執(zhí)行期間發(fā)生的錯誤(Python中一切皆對象,包括異常也是對象)。每當發(fā)生程序錯誤,都會創(chuàng)建一個異常對象。如果編寫了處理該異常的代碼,程序?qū)⒗^續(xù)運行;但是如果未編寫代碼對異常進行處理,程序?qū)⑼V共@示traceback,其中包含有關(guān)異常的報告
異常處理是編程語言或計算機硬件里的一種機制,用于處理軟件或信息系統(tǒng)中出現(xiàn)的異常狀況,即超出程序正常執(zhí)行流程的某些特殊條件。異常處理機制是使用 try-except 代碼塊處理異常,try-except 代碼塊讓Python執(zhí)行指定的操作,同時告訴Python發(fā)生異常時怎么辦。使用try-except 代碼塊時,即便出現(xiàn)異常,程序也將繼續(xù)運行,同時顯示你編寫的友好的錯誤消息,而不是令用戶迷惑的traceback
Python提供了兩個非常重要的功能來處理程序在運行中出現(xiàn)的異常和錯誤。經(jīng)常使用的是try…except語句,拓展一下就是try-except-else-finally,另一個是斷言,即assert
針對 Traceback 的解讀
Traceback 是 Python 錯誤信息的報告,當你的程序?qū)е庐惓r,Python 將打印 Traceback 以幫助你知道哪里出錯了。雖然 Python 的 Traceback 提示信息看著挺復(fù)雜,但是里面存在豐富的信息,可以幫助你診斷和修復(fù)代碼中引發(fā)異常的原因,以及定位到具體哪個文件的哪行代碼出現(xiàn)的錯誤,這樣有助于我們編寫對應(yīng)的異常處理代碼來捕獲異常,并給出合適的錯誤提示信息。所以說學(xué)會看懂 Traceback 信息是非常重要的
下面給出一個案例進行分析,這是一種導(dǎo)致Python引發(fā)異常的簡單錯誤:除0
# calculate.py文件的內(nèi)容
def divide(a, b): # 進行除法運算的函數(shù)return a / b# main.py文件的內(nèi)容
from calculate import dividedivide(5, 0)# 拋出異常:
"""
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 3, in <module>divide(5, 0)File "C:\編程\Python代碼\test\calculate.py", line 2, in dividereturn a / b
ZeroDivisionError: division by zero
"""
- 錯誤輸出的最后一行一般會告訴你引發(fā)了什么類型的異常,以及關(guān)于該異常的一些相關(guān)信息,所以看錯誤信息的最后一行就能獲知到錯誤的原因
- 在上述 traceback 中,最后一行指出的錯誤 ZeroDivisionError 就是一個異常對象,并且給出了具體的描述 division by zero,即表示因為除0,導(dǎo)致程序拋出該異常
- 錯誤輸出的前面幾行的閱讀順序應(yīng)該是由下而上的,因為越往上,離拋出異常的實際位置越遠,越難定位到拋出異常的代碼
- 這一部分要每連續(xù)兩行為一組進行閱讀。其中每組的第1行會告訴你是在哪個文件的、哪個函數(shù)的、哪一行出錯了,也就是會更直接的告訴你錯誤發(fā)生的位置;每組的第2行會把報錯的那一行代碼顯示出來
- 比如在程序里A函數(shù)調(diào)用了B函數(shù),然后B函數(shù)調(diào)用了C函數(shù),而C函數(shù)報錯了,那么 traceback 中的前幾行從下至上的順序來看,會先顯示C函數(shù)的信息,再顯示B函數(shù)的信息,再顯示A函數(shù)的信息,而且每一個函數(shù)的信息都是按兩行來組織的,其中第1行展示位置,第2行展示代碼
- 在上述 traceback 中,前面幾行中的最后一行定位的位置為位于 calculate.py 文件中的第2行,并且位于 divide 函數(shù)代碼塊中,具體拋出異常的代碼為"return a / b"
- 然后我們看上一行,定位的位置為位于 main.py 文件中的第3行,并且位于引入模塊的代碼中,具體拋出異常的代碼為"divide(5, 0)"
- 最上面第一行的內(nèi)容是固定不變的,始終Traceback (most recent call last)”,可以忽略
try-except-else-finally
- try:正常情況下,程序計劃執(zhí)行的語句
- except:程序異常時執(zhí)行的語句
- else:程序無異常即try段代碼正常執(zhí)行后會執(zhí)行該語句
- 當try語句的相關(guān)代碼中的return,continue或break語句被執(zhí)行時,else語句將被忽略
- finally:不管有沒有異常,都會執(zhí)行的語句
- 具體來說,當try,except或else語句的相關(guān)代碼中存在某些跳轉(zhuǎn)語句時,比如break,continue和return,與finally語句相關(guān)的代碼將在這些跳轉(zhuǎn)語句執(zhí)行之前被執(zhí)行
- 在處理 Python 異常的過程中,一些代碼需要始終被執(zhí)行,無論是否有 Python 異常被拋出,或 Python 異常是否被處理。使用finally語句可以達成上述目標,該語句之后的代碼通常與清理工作有關(guān),比如,關(guān)閉打開的文件
運行邏輯1:首先運行try中的代碼塊,當出現(xiàn)異常時,終止try中代碼塊的執(zhí)行,立即執(zhí)行except中的代碼塊,最后執(zhí)行finally中的代碼塊
try:print('輸出:我是try1')a = 5 / 0print('輸出:我是try2')except :print('輸出:我是except')else :print('輸出:我是else')finally :print('輸出:我是finally')# 輸出結(jié)果:
"""
輸出:我是try1
輸出:我是except
輸出:我是finally
"""
運行邏輯2:首先運行try中的代碼塊,當執(zhí)行完畢后,代碼始終沒有拋出異常,繼續(xù)執(zhí)行else中的代碼塊,最后執(zhí)行finally中的代碼塊
try:print('輸出:我是try1')a = 5 / 1print('輸出:我是try2')except :print('輸出:我是except')else :print('輸出:我是else')finally :print('輸出:我是finally')# 輸出結(jié)果:
"""
輸出:我是try1
輸出:我是try2
輸出:我是else
輸出:我是finally
"""
運行邏輯3:函數(shù)中,如果finally語句的相關(guān)代碼中包含了return語句,那么該return語句所返回的值(包括空值None),將取代try,except或else語句相關(guān)代碼中的返回值
因為finally中的代碼塊在設(shè)定上必須執(zhí)行:
- 在try中的代碼塊return之前,會執(zhí)行finally中的語句,try中的return被忽略了,最終返回的值是finally中return值
- try中的代碼塊沒有return語句,執(zhí)行完畢后,會繼續(xù)執(zhí)行else中的語句,在else中的代碼塊return之前,執(zhí)行finally中的語句,else中的return被忽略了,最終返回的值是finally中return值
- try中的代碼塊中拋出異常,被except捕獲,在except中的代碼塊return之前,執(zhí)行finally中的語句,except中的return被忽略了,最終返回的值是finally中return值
def test():try:print('輸出:我是try1')a = 5 / 0print('輸出:我是try2')return 1except :print('輸出:我是except1')return 2print('輸出:我是except2')else :print('輸出:我是else')return 3finally :print('輸出:finally')return 4num = test()
print(num)# 輸出結(jié)果:
"""
輸出:我是try1
輸出:我是except1
輸出:finally
4
"""def test():try:print('輸出:我是try1')a = 5 / 1print('輸出:我是try2')return 1except :print('輸出:我是except')return 2else :print('輸出:我是else')return 3finally :print('輸出:finally')return 4num = test()
print(num)# 輸出結(jié)果:
"""
輸出:我是try1
輸出:我是try2
輸出:finally
4
"""def test():try:print('輸出:我是try1')a = 5 / 1print('輸出:我是try2')# return 1except :print('輸出:我是except')return 2else :print('輸出:我是else1')return 3print('輸出:我是else2')finally :print('輸出:finally')return 4num = test()
print(num)# 輸出結(jié)果:
"""
輸出:我是try1
輸出:我是try2
輸出:我是else1
輸出:finally
4
"""
運行邏輯4:函數(shù)中,finally中不存在return,并且try中代碼塊沒有拋出異常,那么按照 運行邏輯2,函數(shù)返回的是try或者else代碼中先出現(xiàn)的那個return值
因為代碼中程序正常運行,不一定要執(zhí)行else中的代碼塊,如果try中return直接結(jié)束函數(shù)執(zhí)行了,那么就不會執(zhí)行else中的代碼塊
def test():try:print('輸出:我是try1')a = 5 / 1print('輸出:我是try2')return 1except :print('輸出:我是except')return 2else :print('輸出:我是else')return 3finally : # finally中不存在return語句print('輸出:finally')# return 4 num = test()
print(num)# 輸出結(jié)果:
"""
輸出:我是try1
輸出:我是try2
輸出:finally
1
"""def test():try:print('輸出:我是try1')a = 5 / 1print('輸出:我是try2')# return 1except :print('輸出:我是except')return 2else :print('輸出:我是else')return 3finally : # finally中不存在return語句print('輸出:finally')# return 4 num = test()
print(num)# 輸出結(jié)果:
"""
輸出:我是try1
輸出:我是try2
輸出:我是else
輸出:finally
3
"""
運行邏輯5:函數(shù)中,finally中不存在return,并且try中代碼塊產(chǎn)生異常,那么按照 運行邏輯1,函數(shù)返回的是except代碼中的那個return值
因為代碼中程序拋出異常,必須執(zhí)行except中的代碼塊
def test():try:print('輸出:我是try1')a = 5 / 0print('輸出:我是try2')return 1except :print('輸出:我是except')return 2else :print('輸出:我是else')return 3finally : # finally中不存在return語句print('輸出:finally')# return 4 num = test()
print(num)# 輸出結(jié)果:
"""
輸出:我是try1
輸出:我是except
輸出:finally
2
"""
except語句
except語句的機制
每一個try語句,都必須至少有一個except語句,除非存在 finally 語句
- 一般一個try語句后面都需要存在至少一個except語句,但是使用 finally 語句后 except 語句將成為可選的,try語句之后可以沒有任何except語句
try:print("No Error")# 拋出異常:SyntaxError: unexpected EOF while parsingtry:5/0# 拋出異常:SyntaxError: unexpected EOF while parsingtry:print("No Error")finally:print("Something must be done")# 輸出結(jié)果:
"""
No Error
Something must be done
"""try:5/0finally:print("Something must be done")# 輸出結(jié)果:
"""
Something must be done
Traceback (most recent call last):File "C:\編程\Python代碼\test\test.py", line 3, in <module>5/0
ZeroDivisionError: division by zero
"""
使用 else 語句的前提是至少擁有一個 except 語句
- 如果要使用else語句來處理沒有 Python 異常被引發(fā)的情況,那么在try語句之后,必須至少存在一個except語句
try:print("No Error")else:print("must have except")finally:print("Something must be done")# 拋出異常:SyntaxError: invalid syntaxtry:print("No Error")except:print("waiting for error")else:print("must have except")finally:print("Something must be done")# 輸出結(jié)果:
"""
No Error
must have except
Something must be done
"""
一個try…except語句中可以擁有多個except語句,都是最多只有一個 except 語句能與被拋出 Python 異常匹配
- 如果try…except語句擁有多個except語句,那么與 Python 的 if 語句類似,他們會按照先后順序進行匹配,當某一個except語句與被拋出的 Python 異常匹配時,其余的except語句將被忽略
try:5/0except IndexError as e: # 無法捕獲除0異常print("IndexError")except ZeroDivisionError as e: # 可以捕獲除0異常print("ZeroDivisionError1")except ZeroDivisionError as e: # 可以捕獲除0異常print("ZeroDivisionError2")except BaseException as e: # 可以捕獲除0異常print("BaseException")# 輸出結(jié)果:ZeroDivisionError1try:5/0except IndexError as e: # 無法捕獲除0異常print("IndexError")except BaseException as e: # 可以捕獲除0異常print("BaseException")except ZeroDivisionError as e: # 可以捕獲除0異常print("ZeroDivisionError1")except ZeroDivisionError as e: # 可以捕獲除0異常print("ZeroDivisionError2")# 輸出結(jié)果:BaseException
except可以處理一個專門的異常,也可以處理包含在元組中的一組異常
- 可以將多個異常類型放在一個括號中,通過逗號分隔。當try塊引發(fā)其中任何一個異常時,程序都將跳轉(zhuǎn)到該except塊,處理這個異常
try:5 / 0except (IndexError, ZeroDivisionError) as e: # 可以捕獲除0異常print("IndexError or ZeroDivisionError")except BaseException as e: # 可以捕獲除0異常print("ZeroDivisionError1")# 輸出結(jié)果:IndexError or ZeroDivisionError
如果except后沒有指定異常類型,則默認捕獲所有的異常
- 此時捕獲異常范圍為BaseException(后面會提到)
- 這樣的沒有指定異常類型的 except 語句要求必須是最后一個 except 語句,否則程序報錯
- 原因很簡單,如果它位于其他except語句之前,那么一些except語句將失去被執(zhí)行的可能
try:5 / 0except: # 可以捕獲所有異常print("捕獲到異常")# 輸出結(jié)果:捕獲到異常
與所有 except 語句均不匹配的 Python 異常將被重新拋出
- 如果一個 Python 異常被引發(fā),并且與所有的except語句均不匹配,那么該異常將作為未處理的 Python 異常被重新拋出,這可能導(dǎo)致整個程序因此結(jié)束執(zhí)行(當然,finally中的代碼塊依舊會被執(zhí)行)
try:5/0except IndexError as e: # 無法捕獲除0異常print("IndexError")except KeyError as e: # 無法捕獲除0異常print("KeyError")except ValueError as e: # 無法捕獲除0異常print("ValueError")finally:print('輸出:finally')# 拋出異常:
"""
輸出:finally
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 2, in <module>5/0
ZeroDivisionError: division by zero
"""
在 except 語句中引用當前被處理的 Python 異常
在except語句中,使用as關(guān)鍵字可以指定一個與被處理異常綁定(指向異常對象)的標識符(可將其簡單的視為 Python 變量),即可通過該標識符在except語句相關(guān)的代碼中訪問被處理的 Python 異常
此外,在 3.11 或更高版本中,通過sys模塊的exception函數(shù),同樣可在except語句相關(guān)的代碼中訪問當前被處理的 Python 異常
- 異常對象的常用屬性
- args:包含有關(guān)異常的錯誤編號和異常的描述的元組
- strerror:異常的描述
- errno:與異常的錯誤編號
try:open("no_exit_file.txt", "r")except FileNotFoundError as e: # 可以捕獲除0異常print("異常對象:", e)print("異常類型:", type(e))# 異常對象的常用屬性print("異常信息: ", e.args) # 該屬性返回異常的錯誤編號和描述print("異常描述: ", e.strerror) # 該屬性返回異常的描述print("錯誤號: ", e.errno) # 該屬性返回異常的錯誤編號# 輸出結(jié)果:
"""
異常對象: [Errno 2] No such file or directory: 'no_exit_file.txt'
異常類型: <class 'FileNotFoundError'>
異常信息: (2, 'No such file or directory')
異常描述: No such file or directory
錯誤號: 2
"""
except 語句會刪除使用 as 關(guān)鍵字與 Python 異常綁定的標識符
- 如果在某個except語句中使用了as關(guān)鍵字,那么as關(guān)鍵字指定的標識符,將在該except語句的相關(guān)代碼執(zhí)行完畢時被刪除。這意味著標識符僅在except語句中保持其正確性,他不應(yīng)該與同一命名空間的其他標識符重復(fù),以避免一些不必要的錯誤
try:open("no_exit_file.txt", "r")except FileNotFoundError as e: # 可以捕獲除0異常print("異常對象:", e)print("異常類型:", type(e))print(e)# 輸出結(jié)果:
"""
異常對象: [Errno 2] No such file or directory: 'no_exit_file.txt'
異常類型: <class 'FileNotFoundError'>
Traceback (most recent call last):File "C:\編程\Python代碼\test\test.py", line 9, in <module>print(e)
NameError: name 'e' is not defined
"""
finally語句
finally語句執(zhí)行后才能拋出未被處理的異常
當try,except或else語句的相關(guān)代碼引發(fā)不能被處理的 Python 異常時,這些異常不會被立即拋出,他們需要等待finally語句的相關(guān)代碼的執(zhí)行
try:5/0except IndexError as e: # 無法捕獲除0異常print("IndexError")except KeyError as e: # 無法捕獲除0異常print("KeyError")except ValueError as e: # 無法捕獲除0異常print("ValueError")finally:print('輸出:finally')# 拋出異常:
"""
輸出:finally
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 2, in <module>5/0
ZeroDivisionError: division by zero
"""try:5 / 0except ZeroDivisionError as e:print("ZeroDivisionError異常被捕獲")raise efinally:print('輸出:finally')
# 輸出結(jié)果:
"""
ZeroDivisionError異常被捕獲
輸出:finally
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 6, in <module>raise eFile "C:\編程\Python代碼\test\main.py", line 2, in <module>5 / 0
ZeroDivisionError: division by zero
"""try:5 / 1except ZeroDivisionError as e:print("ZeroDivisionError異常被捕獲")else:print('輸出:else')raise ZeroDivisionError("重新拋出的ZeroDivisionError")finally:print('輸出:finally')
# 輸出結(jié)果:
"""
輸出:else
輸出:finally
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 9, in <module>raise ZeroDivisionError("重新拋出的ZeroDivisionError")
ZeroDivisionError: 重新拋出的ZeroDivisionError
"""
finally中執(zhí)行return會導(dǎo)致異常丟失
如果finally語句的相關(guān)代碼中包含了跳轉(zhuǎn)語句,比如break,continue或return,那么這些跳轉(zhuǎn)語句的執(zhí)行,將導(dǎo)致未被except處理的 Python 異常不再被重新拋出,即便這些異常是通過raise語句主動拋出的
3.8 版本之前,在 Python 的finally語句的相關(guān)代碼中,不能使用continue語句
finally中出現(xiàn)return語句,如果try中拋出的異常沒有被捕獲到,按理說當finally執(zhí)行完畢后,應(yīng)該被再次拋出,但finally里執(zhí)行了return,導(dǎo)致異常被丟失,所以實際應(yīng)用中,不推薦在finally中使用return返回
def test():try:5/0print("函數(shù)中的后續(xù)代碼被執(zhí)行")except IndexError as e: # 無法捕獲除0異常print("IndexError")except KeyError as e: # 無法捕獲除0異常print("KeyError")except ValueError as e: # 無法捕獲除0異常print("ValueError")finally:print('輸出:finally')return 1num = test()
print(num)print("后續(xù)代碼被執(zhí)行")# 輸出結(jié)果:
"""
輸出:finally
1
后續(xù)代碼被執(zhí)行
"""
raise 語句
raise 語句以及異常對象
使用 Python 提供的raise語句,開發(fā)人員可以主動拋出(引發(fā))一個 Python 異常
raise 語句基本的語法形式為 raise <exception>
- exception
- 被拋出的 Python 異常對象,或 Python 異常類型如果僅給出異常類型,那么將根據(jù)該類型隱式創(chuàng)建其對應(yīng)的實例,比如,raise ZeroDivisionError的效果等同于raise ZeroDivisionError()
raise ZeroDivisionError# 拋出異常:
"""
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 1, in <module>raise ZeroDivisionError
ZeroDivisionError
"""# 前后對比,發(fā)現(xiàn)效果一致
raise ZeroDivisionError()# 拋出異常:
"""
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 1, in <module>raise ZeroDivisionError()
ZeroDivisionError
"""
創(chuàng)建異常對象時可以指定其魔法方法__str__的返回值
# 判斷ZeroDivisionError()返回的東西是否為ZeroDivisionError的實例對象
ins = ZeroDivisionError()
flag = isinstance(ins, ZeroDivisionError)
print(flag)# 輸出結(jié)果:Trueins = ZeroDivisionError()
dir(ins) # 可以獲知,該實例對象具有魔法方法__str__# 輸出結(jié)果:
"""
['__cause__', '__class__', '__context__', '__delattr__', '__dict__', '__dir__',
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__','__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__suppress_context__', '__traceback__', 'args', 'with_traceback']
"""# 由于調(diào)用print函數(shù)實際是執(zhí)行實例對象的魔法方法__str__,打印結(jié)果為該方法返回值
# 所以可以推斷出實例化異常對象時,傳入的"This is a ZeroDivisionError"
# 實際就是魔法方法__str__的返回值
ins = ZeroDivisionError("This is a ZeroDivisionError")
print(ins) # 輸出結(jié)果:This is a ZeroDivisionErrorins = ZeroDivisionError("This is a ZeroDivisionError")
describe_string = ins.__str__() # 手動調(diào)用魔法方法__str__,打印該方法的返回值
print(describe_string)# 輸出結(jié)果:This is a ZeroDivisionErrordata = 10
ins = ZeroDivisionError("This is a ZeroDivisionError, carry a data: %s" % data )
print(ins) # 輸出結(jié)果:This is a ZeroDivisionError, carry a data: 10
在except語句中,可以使用 raise 語句,將raise語句的exception部分留空,這會將當前被處理的 Python 異常重新拋出(引發(fā))
但是以上做法的效果并不等同于調(diào)用exception函數(shù)的語句raise sys.exception(),或類似于raise err的語句(假設(shè)err為as關(guān)鍵字綁定的標識符),他們會展示不同的回溯(Traceback)信息
# calculate.py文件的內(nèi)容
def divide(a, b): # 進行除法運算的函數(shù)return a / b# main.py文件的內(nèi)容
from calculate import dividedivide(5, 0)# 拋出異常:
"""
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 3, in <module>divide(5, 0)File "C:\編程\Python代碼\test\calculate.py", line 2, in dividereturn a / b
ZeroDivisionError: division by zero
"""# calculate.py文件的內(nèi)容
def divide(a, b): # 進行除法運算的函數(shù)return a / b# main.py文件的內(nèi)容
from calculate import dividetry:divide(5, 0)except ZeroDivisionError:print("ZeroDivisionError異常被捕獲")raise # 使用raise重新拋出異常# 拋出異常:
"""
ZeroDivisionError異常被捕獲
Traceback (most recent call last):File "C:\編程\Python代碼\test\test.py", line 4, in <module>divide(5, 0)File "C:\編程\Python代碼\test\calculate.py", line 2, in dividereturn a / b
ZeroDivisionError: division by zero
"""# calculate.py文件的內(nèi)容
def divide(a, b): # 進行除法運算的函數(shù)return a / b# main.py文件的內(nèi)容
from calculate import dividetry:divide(5, 0)except ZeroDivisionError as e:print("ZeroDivisionError異常被捕獲")raise e # 拋出異常:
"""
ZeroDivisionError異常被捕獲
Traceback (most recent call last):File "C:\編程\Python代碼\test\test.py", line 8, in <module>raise eFile "C:\編程\Python代碼\test\test.py", line 4, in <module>divide(5, 0)File "C:\編程\Python代碼\test\calculate.py", line 2, in dividereturn a / b
ZeroDivisionError: division by zero
"""
拋出異常時,自動創(chuàng)建的異常對象的魔法方法__str__的返回值即為 traceback 最后一行針對該異常的描述信息
5 / 0# 拋出異常:
"""
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 1, in <module>5 / 0
ZeroDivisionError: division by zero
"""try:5 / 0except ZeroDivisionError as e:# 判斷 e 是否為ZeroDivisionError的實例對象flag = isinstance(e, ZeroDivisionError)print(flag)# 手動調(diào)用魔法方法__str__,打印該方法的返回值,進行對比describe_string = e.__str__() print(e)print(describe_string)# 輸出結(jié)果:
"""
True
division by zero
division by zero
"""try:5 / 0except ZeroDivisionError as e:print("自動拋出的異常被捕獲")raise e# 輸出結(jié)果:
"""
自動拋出的異常被捕獲
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 6, in <module>raise eFile "C:\編程\Python代碼\test\main.py", line 2, in <module>5 / 0
ZeroDivisionError: division by zero
"""
自行創(chuàng)建一個異常對象,通過raise拋出,同時攜帶自定義的描述信息
try:5 / 0except ZeroDivisionError as e:print("自動拋出的異常被捕獲")raise ZeroDivisionError("<division by zero>")# 輸出結(jié)果:
"""
自動拋出的異常被捕獲
Traceback (most recent call last):File "C:\編程\Python代碼\test\main.py", line 6, in <module>raise ZeroDivisionError("<division by zero>")
ZeroDivisionError: <division by zero>
"""
raise…from 語句
如果一個 Python 異常已經(jīng)被某個except語句處理,而該except語句的相關(guān)代碼引發(fā)了新的異常,如果新的 Python 異常是通過raise…from語句引發(fā),那么可以為新的 Python 異常指定一個表示原因的異常,用于說明新的 Python 異常是由該異常導(dǎo)致的
raise…from語句可以在其他位置使用,如果位于except語句的相關(guān)代碼中,那么表示原因的異常一般被指定為已被except處理的 Python 異常,此時回溯信息將優(yōu)先展示表示原因的 Python 異常的信息
raise…from 語句基本的語法形式為 raise <newexception> from <causeexception>
-
newexception
- 被拋出的 Python 異常對象,或 Python 異常類型。如果僅給出異常類型,那么將根據(jù)該類型隱式創(chuàng)建其對應(yīng)的實例,比如,raise RuntimeError from ValueError()的效果等同于raise RuntimeError() from ValueError()
-
causeexception
- 表示原因的 Python 異常對象,或 Python 異常類型。如果僅給出異常類型,那么將根據(jù)該類型隱式創(chuàng)建其對應(yīng)的實例,比如,raise RuntimeError() from ValueError的效果等同于raise RuntimeError() from ValueError()
Python 異常的基類 BaseException
在 Python 中,所有異常(表示異常的類)都需要繼承自BaseException或Exception,這包括 Python 的內(nèi)置異常,以及由開發(fā)人員定義的異常(當然,只有少數(shù) Python 異常直接繼承自類BaseException,大部分 Python 異常均繼承自類Exception或類Exception的派生類)
- Python 異?;?BaseException 和 Exception 之間的區(qū)別
- Exception是BaseException類的派生類,他表示不是來自于系統(tǒng)的非正常情況,比如,表示除數(shù)為0的 Python 異常ZeroDivisionError
- 一般情況下,開發(fā)人員僅需要捕獲從Exception類派生的各種 Python 異常,如果將捕獲的范圍擴大到BaseException,那么可能會導(dǎo)致一些意想不到的問題
- 如果except后沒有指定異常類型,則默認捕獲所有的異常,即此時捕獲異常范圍為BaseException, 這樣的沒有指定異常類型的 except 語句要求必須是最后一個 except 語句,否則程序報錯,原因很簡單,如果它位于其他except語句之前,那么一些except語句將失去被執(zhí)行的可能
如果將捕獲異常的范圍擴大到BaseException,可能會導(dǎo)致sys.exit()無法退出 Python 解釋器
import systry:sys.exit() # 嘗試退出程序
except BaseException as e:print(f'捕獲到了異常 {type(e)}')print('sys.exit() 已經(jīng)執(zhí)行,但是程序沒有成功退出')# 輸出結(jié)果:
"""
捕獲到了異常 <class 'SystemExit'>
sys.exit() 已經(jīng)執(zhí)行,但是程序沒有成功退出
"""