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