異常處理
異常就是運(yùn)行期檢測(cè)到的錯(cuò)誤俐末。計(jì)算機(jī)語(yǔ)言針對(duì)可能出現(xiàn)的錯(cuò)誤定義了異常類型趴泌,某種錯(cuò)誤引發(fā)對(duì)應(yīng)的異常時(shí)躲雅,異常處理程序?qū)⒈粏?dòng)炕淮,從而恢復(fù)程序的正常運(yùn)行逢勾。
1. Python 標(biāo)準(zhǔn)異常總結(jié)
- BaseException:所有異常的 基類
- Exception:常規(guī)異常的 基類
- StandardError:所有的內(nèi)建標(biāo)準(zhǔn)異常的基類
- ArithmeticError:所有數(shù)值計(jì)算異常的基類
- FloatingPointError:浮點(diǎn)計(jì)算異常
- OverflowError:數(shù)值運(yùn)算超出最大限制
- ZeroDivisionError:除數(shù)為零
- AssertionError:斷言語(yǔ)句(assert)失敗
- AttributeError:嘗試訪問(wèn)未知的對(duì)象屬性
- EOFError:沒(méi)有內(nèi)建輸入饭耳,到達(dá)EOF標(biāo)記
- EnvironmentError:操作系統(tǒng)異常的基類
- IOError:輸入/輸出操作失敗
- OSError:操作系統(tǒng)產(chǎn)生的異常(例如打開(kāi)一個(gè)不存在的文件)
- WindowsError:系統(tǒng)調(diào)用失敗
- ImportError:導(dǎo)入模塊失敗的時(shí)候
- KeyboardInterrupt:用戶中斷執(zhí)行
- LookupError:無(wú)效數(shù)據(jù)查詢的基類
- IndexError:索引超出序列的范圍
- KeyError:字典中查找一個(gè)不存在的關(guān)鍵字
- MemoryError:內(nèi)存溢出(可通過(guò)刪除對(duì)象釋放內(nèi)存)
- NameError:嘗試訪問(wèn)一個(gè)不存在的變量
- UnboundLocalError:訪問(wèn)未初始化的本地變量
- ReferenceError:弱引用試圖訪問(wèn)已經(jīng)垃圾回收了的對(duì)象
- RuntimeError:一般的運(yùn)行時(shí)異常
- NotImplementedError:尚未實(shí)現(xiàn)的方法
- SyntaxError:語(yǔ)法錯(cuò)誤導(dǎo)致的異常
- IndentationError:縮進(jìn)錯(cuò)誤導(dǎo)致的異常
- TabError:Tab和空格混用
- SystemError:一般的解釋器系統(tǒng)異常
- TypeError:不同類型間的無(wú)效操作
- ValueError:傳入無(wú)效的參數(shù)
- UnicodeError:Unicode相關(guān)的異常
- UnicodeDecodeError:Unicode解碼時(shí)的異常
- UnicodeEncodeError:Unicode編碼錯(cuò)誤導(dǎo)致的異常
- UnicodeTranslateError:Unicode轉(zhuǎn)換錯(cuò)誤導(dǎo)致的異常
異常體系內(nèi)部有層次關(guān)系偶垮,Python異常體系中的部分關(guān)系如下所示:
2. Python標(biāo)準(zhǔn)警告總結(jié)
- Warning:警告的基類
- DeprecationWarning:關(guān)于被棄用的特征的警告
- FutureWarning:關(guān)于構(gòu)造將來(lái)語(yǔ)義會(huì)有改變的警告
- UserWarning:用戶代碼生成的警告
- PendingDeprecationWarning:關(guān)于特性將會(huì)被廢棄的警告
- RuntimeWarning:可疑的運(yùn)行時(shí)行為(runtime behavior)的警告
- SyntaxWarning:可疑語(yǔ)法的警告
- ImportWarning:用于在導(dǎo)入模塊過(guò)程中觸發(fā)的警告
- UnicodeWarning:與Unicode相關(guān)的警告
- BytesWarning:與字節(jié)或字節(jié)碼相關(guān)的警告
- ResourceWarning:與資源使用相關(guān)的警告
3. try - except 語(yǔ)句
try:
檢測(cè)范圍
except Exception[as reason]:
出現(xiàn)異常后的處理代碼
try 語(yǔ)句按照如下方式工作:
- 首先,執(zhí)行
try
子句(在關(guān)鍵字try
和關(guān)鍵字except
之間的語(yǔ)句) - 如果沒(méi)有異常發(fā)生叫惊,忽略
except
子句款青,try
子句執(zhí)行后結(jié)束。 - 如果在執(zhí)行
try
子句的過(guò)程中發(fā)生了異常霍狰,那么try
子句余下的部分將被忽略抡草。如果異常的類型和except
之后的名稱相符,那么對(duì)應(yīng)的except
子句將被執(zhí)行蔗坯。最后執(zhí)行try - except
語(yǔ)句之后的代碼康震。 - 如果一個(gè)異常沒(méi)有與任何的
except
匹配,那么這個(gè)異常將會(huì)傳遞給上層的try
中步悠。
【例子】
try:
f = open('test.txt')
print(f.read())
f.close()
except OSError:
print('打開(kāi)文件出錯(cuò)')
# 打開(kāi)文件出錯(cuò)
[root@10 python]# ls test.txt
ls: 無(wú)法訪問(wèn)'test.txt': 沒(méi)有那個(gè)文件或目錄
[root@10 python]# py try-except.py
打開(kāi)文件出錯(cuò)
[root@10 python]# echo -n "測(cè)試" > test.txt
[root@10 python]# py try-except.py
測(cè)試
[root@10 python]#
這段代碼的作用是嘗試打開(kāi)名為"test.txt"的文件签杈,如果打開(kāi)成功,則讀取文件內(nèi)容并打印出來(lái)鼎兽,最后關(guān)閉文件答姥。如果打開(kāi)文件出錯(cuò)(比如文件不存在或無(wú)法訪問(wèn)),則會(huì)捕獲 OSError
異常并打印出"打開(kāi)文件出錯(cuò)"谚咬。
解釋一下代碼的執(zhí)行過(guò)程:
- 首先鹦付,嘗試打開(kāi)名為"test.txt"的文件,如果文件存在且可以被訪問(wèn)择卦,則執(zhí)行下面的代碼塊敲长;否則跳到
except
語(yǔ)句。 - 打開(kāi)文件后秉继,使用
f.read()
方法讀取文件內(nèi)容祈噪,并通過(guò)print()
函數(shù)打印出來(lái)。 - 最后尚辑,使用
f.close()
關(guān)閉文件辑鲤,確保資源被正確釋放。 - 如果在
try
代碼塊中發(fā)生了OSError
異常(比如文件無(wú)法打開(kāi))杠茬,那么程序會(huì)跳到except
語(yǔ)句月褥,并執(zhí)行其中的代碼,打印出"打開(kāi)文件出錯(cuò)"瓢喉。
注意:使用 try-except
結(jié)構(gòu)可以捕獲指定類型的異常宁赤,并在出現(xiàn)異常時(shí)執(zhí)行特定的錯(cuò)誤處理代碼,避免程序崩潰栓票。
【例子】
try:
f = open('test.txt')
print(f.read())
f.close()
except OSError as error:
print('打開(kāi)文件出錯(cuò)\n原因是:' + str(error))
# 打開(kāi)文件出錯(cuò)
# 原因是:[Errno 2] No such file or directory: 'test.txt'
[root@10 python]# rm -f test.txt
[root@10 python]# py try-except_2.py
打開(kāi)文件出錯(cuò)
原因是:[Errno 2] No such file or directory: 'test.txt'
[root@10 python]#
這段代碼與之前的代碼相似决左,但在 except
塊中做了一些修改。現(xiàn)在,代碼會(huì)捕獲 OSError
異常哆窿,并將異常信息打印出來(lái)链烈。
解釋一下代碼的執(zhí)行過(guò)程:
- 首先,嘗試打開(kāi)名為"test.txt"的文件挚躯,如果文件存在且可以被訪問(wèn)强衡,則執(zhí)行下面的代碼塊;否則跳到
except
語(yǔ)句码荔。 - 打開(kāi)文件后漩勤,使用
f.read()
方法讀取文件內(nèi)容,并通過(guò)print()
函數(shù)打印出來(lái)缩搅。 - 最后越败,使用
f.close()
關(guān)閉文件,確保資源被正確釋放硼瓣。 - 如果在
try
代碼塊中發(fā)生了OSError
異常(比如文件無(wú)法打開(kāi))究飞,那么程序會(huì)跳到except
語(yǔ)句,并執(zhí)行其中的代碼堂鲤。 -
as error
表示將捕獲的異常對(duì)象賦值給變量error
亿傅。 - 在
except
塊中,使用print()
函數(shù)將錯(cuò)誤提示信息打印出來(lái)瘟栖,包括捕獲到的異常信息葵擎。
這樣,即可打印出出錯(cuò)的原因半哟,幫助調(diào)試程序或提供錯(cuò)誤信息酬滤。
3.1 一個(gè)try
語(yǔ)句可能包含多個(gè)except
子句,分別來(lái)處理不同的特定的異常寓涨。最多只有一個(gè)分支會(huì)被執(zhí)行盯串。**
【例子】
try:
int("abc")
s = 1 + '1'
f = open('test.txt')
print(f.read())
f.close()
except OSError as error:
print('打開(kāi)文件出錯(cuò)\n原因是:' + str(error))
except TypeError as error:
print('類型出錯(cuò)\n原因是:' + str(error))
except ValueError as error:
print('數(shù)值出錯(cuò)\n原因是:' + str(error))
# 數(shù)值出錯(cuò)
# 原因是:invalid literal for int() with base 10: 'abc'
[root@10 python]# ls test.txt
ls: 無(wú)法訪問(wèn)'test.txt': 沒(méi)有那個(gè)文件或目錄
[root@10 python]# py try-except_3.py
數(shù)值出錯(cuò)
原因是:invalid literal for int() with base 10: 'abc'
[root@10 python]# sed -i.bak 's|abc|123|' try-except_3.py
[root@10 python]# sed -n '/int(.....)/'p try-except_3.py
int("123")
[root@10 python]# py try-except_3.py
類型出錯(cuò)
原因是:unsupported operand type(s) for +: 'int' and 'str'
[root@10 python]# sed -i.bak2 "s#'1'#1#" try-except_3.py
[root@10 python]# sed -n '/s =/'p try-except_3.py
s = 1 + 1
[root@10 python]# py try-except_3.py
打開(kāi)文件出錯(cuò)
原因是:[Errno 2] No such file or directory: 'test.txt'
[root@10 python]# echo -n "測(cè)試" > test.txt
[root@10 python]# py try-except_3.py
測(cè)試
[root@10 python]#
這段代碼中使用了多個(gè) except
塊來(lái)捕獲不同類型的異常,并打印出對(duì)應(yīng)的錯(cuò)誤信息戒良。
解釋一下代碼的執(zhí)行過(guò)程:
- 首先嘴脾,嘗試將字符串 "abc" 轉(zhuǎn)換為整數(shù),由于該字符串不能被正確轉(zhuǎn)換蔬墩,拋出了
ValueError
異常。這時(shí)耗拓,程序會(huì)跳到第一個(gè)except
塊中拇颅。 - 在第一個(gè)
except
塊中,使用print()
函數(shù)打印出錯(cuò)誤提示信息乔询,包括捕獲的異常信息樟插。在這里,打印出了“數(shù)值出錯(cuò)\n原因是:invalid literal for int() with base 10: 'abc'”。 - 接下來(lái)黄锤,由于前面的語(yǔ)句發(fā)生了異常搪缨,程序不會(huì)繼續(xù)執(zhí)行剩余的代碼。
- 注意鸵熟,第二個(gè)和第三個(gè)
except
塊不會(huì)執(zhí)行副编,因?yàn)檫@里拋出的異常是ValueError
,而不是OSError
或TypeError
流强。
使用多個(gè) except
塊可以根據(jù)不同的異常類型痹届,進(jìn)行不同的錯(cuò)誤處理。這樣能夠更精確地捕獲和處理異常打月,提高代碼的健壯性队腐。
3.2 try-except-else 語(yǔ)句
【例子】
dict1 = {'a': 1, 'b': 2, 'v': 22}
try:
x = dict1['y']
except LookupError:
print('查詢錯(cuò)誤')
except KeyError:
print('鍵錯(cuò)誤')
else:
print(x)
# 查詢錯(cuò)誤
[root@10 python]# py try-except-else.py
查詢錯(cuò)誤
[root@10 python]# vi try-except-else.py
[root@10 python]# cat try-except-else.py
dict1 = {'a': 1, 'b': 2, 'v': 22}
try:
x = dict1['y']
except KeyError:
print('鍵錯(cuò)誤')
except LookupError:
print('查詢錯(cuò)誤')
else:
print(x)
[root@10 python]# py try-except-else.py
鍵錯(cuò)誤
[root@10 python]# sed -i.bak "s/'y'/'a'/" try-except-else.py
[root@10 python]# sed -n '/a/'p try-except-else.py
dict1 = {'a': 1, 'b': 2, 'v': 22}
x = dict1['a']
[root@10 python]# py try-except-else.py
1
[root@10 python]#
這段代碼中使用了 try-except-else
結(jié)構(gòu)來(lái)處理字典的查詢操作。
解釋一下代碼的執(zhí)行過(guò)程:
- 首先奏篙,嘗試從
dict1
字典中查詢鍵'y'
對(duì)應(yīng)的值柴淘,由于該鍵不存在,拋出了KeyError
異常秘通。這時(shí)为严,程序會(huì)跳到第一個(gè)except
塊中。 - 在第一個(gè)
except
塊中充易,使用print()
函數(shù)打印出錯(cuò)誤提示信息梗脾,即"查詢錯(cuò)誤"。 - 由于發(fā)生了異常盹靴,程序不會(huì)繼續(xù)執(zhí)行
else
塊中的代碼炸茧。 - 注意,這里使用了
LookupError
作為第一個(gè)except
塊的異常類型稿静,它是KeyError
的父類梭冠,因此可以捕獲KeyError
異常。 - 所以改备,使用多個(gè)except代碼塊時(shí)控漠,必須堅(jiān)持對(duì)其規(guī)范排序,要從最具針對(duì)性的異常到最通用的異常悬钳。
通過(guò) try-except-else
結(jié)構(gòu)盐捷,我們可以在 try
塊中嘗試執(zhí)行某個(gè)操作,如果發(fā)生異常默勾,則執(zhí)行特定的異常處理代碼(如 except
塊)碉渡,如果沒(méi)有發(fā)生異常,則執(zhí)行其他的代碼(如 else
塊)母剥。這樣可以使代碼更加靈活和健壯滞诺。
3.3 一個(gè) except
子句可以同時(shí)處理多個(gè)異常形导,這些異常將被放在一個(gè)括號(hào)里成為一個(gè)元組。
【例子】
try:
int("abc")
s = 1 + '1'
f = open('test.txt')
print(f.read())
f.close()
except (OSError, TypeError, ValueError) as error:
print('出錯(cuò)了习霹!\n原因是:' + str(error))
# 出錯(cuò)了
# 原因是:invalid literal for int() with base 10: 'abc'
這段代碼中使用了一個(gè) except
塊來(lái)捕獲多個(gè)異常類型朵耕,并打印出對(duì)應(yīng)的錯(cuò)誤信息。
解釋一下代碼的執(zhí)行過(guò)程:
- 首先淋叶,嘗試將字符串 "abc" 轉(zhuǎn)換為整數(shù)阎曹,由于該字符串不能被正確轉(zhuǎn)換,拋出了
ValueError
異常爸吮。 - 接著芬膝,由于前面的語(yǔ)句發(fā)生了異常,程序不會(huì)繼續(xù)執(zhí)行剩余的代碼形娇。
- 在
except
塊中锰霜,使用(OSError, TypeError, ValueError)
表示同時(shí)捕獲這三種異常類型。然后桐早,將捕獲的異常對(duì)象賦值給變量error
癣缅。 - 在
except
塊中,使用print()
函數(shù)打印出錯(cuò)誤提示信息哄酝,包括捕獲的異常信息友存。在這里,打印出了“出錯(cuò)了陶衅!\n原因是:invalid literal for int() with base 10: 'abc'”屡立。
通過(guò)使用括號(hào)((OSError, TypeError, ValueError)
)將多個(gè)異常類型放在一起,可以在單個(gè) except
塊中捕獲多個(gè)異常搀军。這樣簡(jiǎn)化了代碼膨俐,并減少了重復(fù)的代碼判斷。
元組內(nèi)的異常類型順序?qū)Y(jié)果沒(méi)有影響罩句。
try
語(yǔ)句塊中的語(yǔ)句段順序?qū)Y(jié)果有影響焚刺,誰(shuí)在前并當(dāng)發(fā)生異常時(shí),就先輸出誰(shuí)并終止代碼執(zhí)行门烂。
感謝你的指正乳愉,我會(huì)更加準(zhǔn)確地回答你的問(wèn)題。非常抱歉之前的回答有誤屯远。謝謝蔓姚!
4. try - except - finally 語(yǔ)句
try:
檢測(cè)范圍
except Exception[as reason]:
出現(xiàn)異常后的處理代碼
finally:
無(wú)論如何都會(huì)被執(zhí)行的代碼
不管try
子句里面有沒(méi)有發(fā)生異常,finally
子句都會(huì)執(zhí)行慨丐。
【例子】如果一個(gè)異常在try
子句里被拋出赂乐,而又沒(méi)有任何的except
把它截住,那么這個(gè)異常會(huì)在finally
子句執(zhí)行后被拋出咖气。
def divide(x, y):
try:
result = x / y
print("result is", result)
except ZeroDivisionError:
print("division by zero!")
finally:
print("executing finally clause")
divide(2, 1)
# result is 2.0
# executing finally clause
divide(2, 0)
# division by zero!
# executing finally clause
divide("2", "1")
# executing finally clause
# TypeError: unsupported operand type(s) for /: 'str' and 'str'
[root@10 python]# py try-except-finally.py
result is 2.0
executing finally clause
division by zero!
executing finally clause
executing finally clause
Traceback (most recent call last):
File "/root/python/try-except-finally.py", line 17, in <module>
divide("2", "1")
File "/root/python/try-except-finally.py", line 3, in divide
result = x / y
TypeError: unsupported operand type(s) for /: 'str' and 'str'
[root@10 python]#
這段代碼中挨措,divide
函數(shù)用于執(zhí)行兩個(gè)數(shù)的除法操作。它使用了try-except-finally
語(yǔ)句塊來(lái)處理可能出現(xiàn)的異常情況崩溪。
當(dāng)傳入的兩個(gè)參數(shù)x
和y
都是數(shù)值類型時(shí)浅役,try
塊中的除法操作將正常執(zhí)行,結(jié)果將存儲(chǔ)在result
變量中伶唯,并通過(guò)print語(yǔ)句打印出來(lái)觉既。然后,無(wú)論是否有異常發(fā)生乳幸,finally
塊中的代碼都會(huì)執(zhí)行瞪讼,打印出"executing finally clause"的消息。
當(dāng)傳入的y
為0時(shí)粹断,會(huì)發(fā)生ZeroDivisionError
異常符欠,except
塊中的代碼將執(zhí)行,打印出"division by zero!"的消息瓶埋。然后希柿,無(wú)論是否有異常發(fā)生,finally
塊中的代碼仍然會(huì)執(zhí)行养筒,打印出"executing finally clause"的消息曾撤。
當(dāng)傳入的參數(shù)x和y都是字符串類型時(shí),由于字符串不支持除法操作晕粪,將拋出TypeError異常挤悉。在這種情況下,except
塊中的代碼不會(huì)執(zhí)行巫湘,而是直接跳過(guò)并執(zhí)行finally
塊中的代碼装悲,打印出"executing finally clause"的消息,并輸出錯(cuò)誤信息剩膘。
5. try - except - else 語(yǔ)句
如果在try
子句執(zhí)行時(shí)沒(méi)有發(fā)生異常衅斩,Python將執(zhí)行else
語(yǔ)句后的語(yǔ)句。
try:
檢測(cè)范圍
except:
出現(xiàn)異常后的處理代碼
else:
如果沒(méi)有異常執(zhí)行這塊代碼
使用except
而不帶任何異常類型怠褐,這不是一個(gè)很好的方式畏梆,我們不能通過(guò)該程序識(shí)別出具體的異常信息,因?yàn)樗东@所有的異常奈懒。
try:
檢測(cè)范圍
except(Exception1[, Exception2[,...ExceptionN]]]):
發(fā)生以上多個(gè)異常中的一個(gè)奠涌,執(zhí)行這塊代碼
else:
如果沒(méi)有異常執(zhí)行這塊代碼
【例子】
try:
fh = open("testfile.txt", "w")
fh.write("這是一個(gè)測(cè)試文件,用于測(cè)試異常!!")
except IOError:
print("Error: 沒(méi)有找到文件或讀取文件失敗")
else:
print("內(nèi)容寫(xiě)入文件成功")
fh.close()
# 內(nèi)容寫(xiě)入文件成功
這段代碼是一個(gè)文件操作的示例磷杏,讓我解釋一下它的執(zhí)行流程:
首先溜畅,代碼嘗試打開(kāi)文件 "testfile.txt" 并以寫(xiě)入模式打開(kāi)。如果文件不存在极祸,將創(chuàng)建一個(gè)新文件慈格。這部分邏輯被放在
try
塊中怠晴。如果文件打開(kāi)成功,代碼會(huì)執(zhí)行
fh.write("這是一個(gè)測(cè)試文件浴捆,用于測(cè)試異常!!")
來(lái)向文件寫(xiě)入內(nèi)容蒜田。如果文件打開(kāi)或?qū)懭脒^(guò)程中發(fā)生了輸入/輸出(IO)錯(cuò)誤,例如文件無(wú)法找到或無(wú)法讀取选泻,那么程序會(huì)跳轉(zhuǎn)到
except IOError
塊中冲粤,并打印 "Error: 沒(méi)有找到文件或讀取文件失敗"。如果文件打開(kāi)和寫(xiě)入都成功完成且未發(fā)生任何錯(cuò)誤页眯,那么程序會(huì)執(zhí)行
else
塊中的代碼梯捕,打印 "內(nèi)容寫(xiě)入文件成功",然后關(guān)閉文件窝撵。
總結(jié)起來(lái)傀顾,這段代碼嘗試打開(kāi)文件并寫(xiě)入內(nèi)容,如果在這個(gè)過(guò)程中發(fā)生了IO錯(cuò)誤忿族,則會(huì)捕獲并處理異常锣笨。如果沒(méi)有發(fā)生錯(cuò)誤,代碼會(huì)執(zhí)行相應(yīng)的成功邏輯道批,并在最后關(guān)閉文件错英。
注意:else
語(yǔ)句的存在必須以except
語(yǔ)句的存在為前提,在沒(méi)有except
語(yǔ)句的try
語(yǔ)句中使用else
語(yǔ)句隆豹,會(huì)引發(fā)語(yǔ)法錯(cuò)誤椭岩。
6. raise語(yǔ)句
Python 使用raise
語(yǔ)句拋出一個(gè)指定的異常。
【例子】
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
# An exception flew by!
這段代碼演示了如何使用 raise
語(yǔ)句主動(dòng)引發(fā)一個(gè)異常璃赡,并使用 try-except
結(jié)構(gòu)捕獲并處理該異常判哥。讓我解釋一下它的執(zhí)行流程:
在
try
塊中,使用raise
語(yǔ)句引發(fā)了一個(gè)NameError
異常碉考,異常的錯(cuò)誤信息為'HiThere'
塌计。因?yàn)樵?
except NameError
中指定了捕獲NameError
類型的異常,所以代碼會(huì)跳轉(zhuǎn)到該except
塊中侯谁。在
except
塊中锌仅,會(huì)打印出字符串 "An exception flew by!"。
總結(jié)起來(lái)墙贱,這段代碼通過(guò) raise
語(yǔ)句主動(dòng)引發(fā)了一個(gè) NameError
異常热芹,并在 try-except
結(jié)構(gòu)中捕獲和處理了該異常。當(dāng)異常被引發(fā)時(shí)惨撇,代碼會(huì)跳轉(zhuǎn)到與異常類型匹配的 except
塊中執(zhí)行相應(yīng)的邏輯伊脓。