Python學習筆記(十三):例外處理機制
關于Python的例外處理機制
- Python學習筆記(十三):例外處理機制
- 一.例外處理機制
- 常見例外型別
- 二.例外處理
- try...except
- 例外類的繼承體系
- 多例外捕獲
- 訪問例外資訊
- else 塊
- 三.資源回收
- 四.獲取例外資訊
- sys.exc_info()
- traceback模塊
- 五.raise陳述句
- 六.例外處理機制的正確使用
一.例外處理機制
例外處理機制,在程式運行出現錯誤時,讓Python解釋器執行事先準備好的除錯程式,進而嘗試恢復程式的執行
借助例外處理機制,可以做到在程式崩潰前將記憶體中的資料寫入檔案、關閉打開的檔案、釋放分配的記憶體等
Python例外處理機制,可以讓程式中的例外處理代碼和正常業務代碼分離,,提高程式的健壯性
常見例外型別
語法錯誤
SyntaxError
語法錯誤屬于真正意義上的錯誤,只有將程式中的所有語法錯誤全部糾正,程式才能執行
運行時錯誤
程式在語法上是正確的,但在運行時發生了錯誤,把這種運行時產生錯誤的情況叫做例外(Exceptions)
常見例外型別
| 例外型別 | 含義 |
|---|---|
| AssertionError | 當 assert 關鍵字后的條件為假時,程式運行會停止并出現例外 |
| AttributeError | 當訪問的物件屬性不存在 |
| IndexError | 索引超出序列范圍 |
| KeyError | 字典中查找一個不存在的關鍵字 |
| NameError | 訪問一個未宣告的變數 |
| TypeError | 不同型別資料之間的無效操作 |
| ZeroDivisionError | 除法運算中除數為 0 |
二.例外處理
try…except
try:
#業務代碼
...
except (Error1, Error2, ...) as e:
alert 輸入不合法
goto retry
1.在執行try塊里的業務代碼時出現例外,系統自動生成一個例外物件,會被提交給Python解釋器,這個程序被稱為引發例外
2.當解釋器收到例外物件時,尋找處理該例外物件的except塊,找到合適的 except塊,則把該例外物件交給該except塊處理,這個程序被稱為捕獲例外
3.如果解釋器找不到捕獲例外的except塊,則運行時環境終止,解釋器退出
4.不管程式代碼塊是否處于try塊中,包括except塊中的代碼;只要執行該代碼塊時出現了例外,系統總會自動生成一個 Error 物件
5.如果程式沒有為這段代碼定義任何的 except 塊,解釋器無法找到處理該例外的except塊,程式在此退出
例外類的繼承體系
每個except塊都是專門用于處理該例外類及其子類的例外實體
當解釋器接收到例外物件后,會依次判斷該例外物件是否是 except 塊后的例外類或其子類的實體,如果是,將呼叫該except塊;否則,再次將該例外物件和下一個except塊里的例外類進行比較
Python的所有例外類都從 BaseException 派生而來,有豐富的例外類,這些例外類之間有嚴格的繼承關系

Python的所有例外類的基類是 BaseException,如果要實作自定義例外,應該繼承 Exception 類
BaseException 的主要子類就是Exception,不管是系統的例外類,還是用戶自定義的例外類,都應該從Exception派生
import sys
try:
a = int(sys.argv[1])
b = int(sys.argv[2])
c = a / b
print("輸入的兩個數相除的結果是:", c )
except IndexError:
print("索引錯誤:輸入的引數個數不夠")
except ValueError:
print("數值錯誤:只能接收整數引數")
except ArithmeticError:
print("算術錯誤")
except Exception:
print("未知例外")
匯入sys模塊,通過 sys 模塊的argv串列來獲取運行程式時提供的引數
sys.argv[0] 通常代表正在運行的程式名,sys.argv[1]代表運行程式所提供的第一個引數,sys.argv[2]代表運行程式所提供的第二個參數……
-
在運行該程式時輸入的引數不夠,會發生索引錯誤,呼叫 IndexError 對應的 except 塊處理該例外
-
在運行該程式時輸入的引數不是數字,而是字母,將發生數值錯誤,呼叫 ValueError 對應的 except 塊處理該例外
-
在運行該程式時輸入的第二個引數是 0,將發生除 0 例外,呼叫 ArithmeticError 對應的 except 塊處理該例外
-
在程式運行時出現其他例外,該例外物件總是 Exception 類或其子類的實體,呼叫 Exception 對應的 except 塊處理該例外
進行例外捕獲時應該把Exception類對應的except塊放在最后,所有父類例外的except塊都應該排在子類例外的except塊的后面
先捕獲小例外,再捕獲大例外
多例外捕獲
在使用一個 except 塊捕獲多種型別的例外時,將多個例外類用圓括號括起來,中間用逗號隔開,就是構建多個例外類的元組
import sys
try:
a = int(sys.argv[1])
b = int(sys.argv[2])
c = a / b
print("輸入的兩個數相除的結果是:", c )
except (IndexError, ValueError, ArithmeticError):
print("程式發生了陣列越界、數字格式例外、算術例外之一")
except: #*
print("未知例外")
*只有except關鍵字,省略例外類的except陳述句也是合法的,表示可捕獲所有型別的例外,一般會作為例外捕獲的最后一個except塊
訪問例外資訊
在 except 塊中訪問例外物件的相關資訊,通過為例外物件宣告變數來實作
所有的例外物件都包含了如下幾個常用屬性和方法:
args:該屬性回傳例外的錯誤編號和描述字串
errno:該屬性回傳例外的錯誤編號
strerror:該屬性回傳例外的描述宇符串
with_traceback():通過該方法可處理例外的傳播軌跡資訊
def test():
try:
fis = open("you.txt");
except Exception as e:
print(e.args) # 訪問例外的錯誤編號和詳細資訊
print(e.errno) # 訪問例外的錯誤編號
print(e.strerror) # 訪問例外的詳細資訊
test()
(2, 'No such file or directory')
2
No such file or directory
訪問例外物件,在單個例外類或例外類元組(多例外捕獲)之后使用 as 加上例外變數
else 塊
在Python的例外處理流程中還可添加一個 else 塊,當 try 塊沒有出現例外時,程式會執行 else 塊
當 try 塊沒有例外,而 else 塊有例外時,體現出 else 塊的作用
else 必須和 try except 搭配使用
def else_test():
s = input('輸入除數:')
result = 20 / int(s)
print('20除以%s的結果是: %g' % (s , result))
def wrong_main():
try:
print('try塊的代碼,沒有例外')
else_test() # 將else_test放在try塊代碼的后面
except:
print('程式出現例外')
def right_main():
try:
print('try塊的代碼,沒有例外')
except:
print('程式出現例外')
else:
else_test() # 將else_test放在else塊中
wrong_main()
right_main()
try塊的代碼,沒有例外
輸入除數:0
程式出現例外
try塊的代碼,沒有例外
輸入除數:0
ZeroDivisionError: division by zero
定義了一個else_test() 函式,在運行時需要接收用戶輸入的引數,輸入資料的不同可能導致例外
定義了right_main() 和 wrong_main() 兩個函式,right_main() 將 else_test() 函式放在 else 塊內;wrong_main() 將 else_test() 函式放在 try 塊的代碼的后面
當 try 塊和 else 塊都沒有例外時,將 else_test() 函式放在 try 塊的代碼的后面和放在 else 塊中沒有任何區別
如果輸入的資料讓 else_test() 函式出現例外(try 塊沒有例外)
將 else_test() 函式放在 try 塊的代碼的后面,此時 else_test() 函式運行產生的例外將會被 try 塊對應的 except 捕獲,這是例外處理機制的執行流程
將 else_test() 函式放在 else 塊中,當 else_test() 函式出現例外時,程式沒有 except 塊來處理該例外,該例外會傳播給解釋器,導致程式中止
放在 else 塊中的代碼所引發的例外不會被 except 塊捕獲,如果希望某段代碼的例外能被后面的 except 塊捕獲,那么就應該將這段代碼放在 try 塊的代碼之后
三.資源回收
finally只要求和try搭配使用,沒有規定該結構中是否包含except和 else(else 必須和 try except 搭配使用)
finally陳述句在例外處理機制中的功能:無論 try 塊是否發生例外,都要進入 finally陳述句,執行其中的代碼塊
當try塊中的程式打開了一些物理資源(檔案、資料庫連接等)時,由于這些資源必須手動回收,回收作業通常就放在finally塊中
Python垃圾回識訓制只能回收變數、類物件占用的記憶體,無法自動完成關閉檔案、資料庫連接等這些的作業
try:
a = int(input("輸入 a 的值:"))
print(20/a)
except:
print("發生例外")
else:
print("執行 else 塊中的代碼")
finally :
print("執行 finally 塊中的代碼")
輸入 a 的值:a
發生例外
執行 finally 塊中的代碼
當try塊發生例外,且沒有合適的except處理例外時,finally 塊中的代碼也會得到執行
try:
print(20/0)
finally :
print("執行 finally 塊中的代碼")
執行 finally 塊中的代碼
ZeroDivisionError: division by zero
當try塊中代碼發生例外,在程式崩潰前Python解釋器也會執行finally塊中的代碼
四.獲取例外資訊
sys.exc_info()
exc_info()會將當前的例外資訊以元組的形式回傳,該元組中包含 3 個元素,為 type、value 和 traceback
type:例外型別的名稱,是BaseException的子類
value:捕獲到的例外實體
traceback:一個traceback物件
import sys #引入sys模塊
try:
x = int(input("輸入一個被除數:"))
print("30除以",x,"等于",30/x)
except:
print(sys.exc_info())
print("其他例外")
輸入一個被除數:0
(<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero',), <traceback object at 0x02B5FFA8>)
其他例外
第 1 個元素是一個ZeroDivisionError 類
第 2 個元素是例外型別 ZeroDivisionError 類的一個實體
第 3 個元素為一個 traceback 物件
通過前2個元素可以看出拋出的例外型別以及描述資訊,第 3 個元素是一個 traceback 物件,無法直接看出有關例外的資訊,還需要對其做進一步處理
traceback模塊
traceback模塊可以用來查看例外的傳播軌跡,追蹤例外觸發的源頭
在實際應用程式的開發中,一個大的功能需要由多個函式或方法來共同實作,在最終編程模型中,很多物件將通過一系列函式或方法呼叫來實作通信,執行任務
當應用程式運行時,經常會發生一系列函式或方法呼叫,例外的傳播則相反,只要例外沒有被完全捕獲(例外沒有被捕獲,或者例外被處理后重新引發了新例外)
例外就從發生例外的函式或方法逐漸向外傳播,先傳給該函式或方法的呼叫者,該函式或方法的呼叫者再傳給其呼叫者,最后傳到解釋器,此時解釋器會中止該程式,并列印例外的傳播軌跡資訊
用traceback模塊查看例外傳播軌跡,首先將 traceback 模塊引入,提供了兩個常用方法:
traceback.print_exc():將例外傳播軌跡資訊輸出到控制臺或指定檔案中
format_exc():將例外傳播軌跡資訊轉換成字串
常用的 print_exc() 是 print_exc([limit[, file]]) 省略了 limit、file 兩個引數的形式
print_exc([limit[, file]]) 的完整形式是 print_exception(etype, value, tb[,limit[, file]])
etype:指定例外型別
value:指定例外值
tb:指定例外的traceback 資訊
print_exc([limit[, file]]) 相當于如下形式:
print_exception(sys.exc_etype, sys.exc_value, sys.exc_tb[, limit[, file]])
print_exc([limit[, file]]) 會自動處理當前 except 塊所捕獲的例外
limit:用于限制顯示例外傳播的層數,比如函式 A 呼叫函式 B,函式 B 發生了例外,如果指定 limit=1,只顯示函式 A 里面發生的例外,如果不設定則默認全部顯示
file:指定將例外傳播軌跡資訊輸出到指定檔案中,如果不指定該引數,則默認輸出到控制臺
import traceback # 匯入trackback模塊
class SelfException(Exception): pass
def main():
firstMethod()
def firstMethod():
secondMethod()
def secondMethod():
thirdMethod()
def thirdMethod():
raise SelfException("自定義例外資訊")
try:
main()
except:
traceback.print_exc() # 捕捉例外,并將例外傳播資訊輸出控制臺
traceback.print_exc(file=open('log.txt', 'a')) # 捕捉例外,并將例外傳播資訊輸出指定檔案中
先匯入traceback模塊,接下來使用except捕獲程式的例外,并使用traceback 的 print_exc() 方法輸出例外傳播資訊,將它輸出到控制臺和指定檔案中
運行上面程式,可以看到在控制臺輸出例外傳播資訊,在程式目錄下生成了一個 log.txt 檔案,該檔案中同樣記錄了例外傳播資訊
五.raise陳述句
使用raise陳述句在程式中手動設定例外
raise [exceptionName [(reason)]]
用[]括起來的為可選引數,其作用是指定拋出的例外名稱,以及例外資訊的相關描述,如果可選引數全部省略,則 raise 會把當前錯誤原樣拋出;如果僅省略 (reason),則在拋出例外時,將不附帶任何的例外描述資訊
raise 陳述句三種常用用法:
raise:單獨一個raise引發當前背景關系中捕獲的例外(比如except 塊中)或默認引發 RuntimeError 例外
raise 例外類名稱:raise 后帶一個例外類名稱,表示引發執行型別的例外
raise 例外類名稱(描述資訊):在引發指定型別的例外的同時,附帶例外的描述資訊
raise 陳述句引發的例外通常用 try except(else finally)例外處理結構來捕獲并進行處理
try:
a = input("輸入一個數:")
if(not a.isdigit()): #判斷輸入的是否為數字
raise ValueError("a 必須是數字")
except ValueError as e:
print("引發例外:",repr(e))
輸入一個數:a
引發例外: ValueError('a 必須是數字',)
當輸入的不是數字時,進入 if 判斷陳述句,并執行 raise 引發 ValueError 例外,因為其位于 try 塊中,raise拋出的例外會被try捕獲,并由except塊進行處理
雖然程式中使用了 raise 陳述句引發例外,但程式的執行是正常的,手動拋出的例外并不會導致程式崩潰
使用 raise 陳述句時可以不帶引數
try:
a = input("輸入一個數:")
if(not a.isdigit()): #判斷輸入的是否為數字
raise ValueError("a 必須是數字")
except ValueError as e:
print("引發例外:",repr(e))
raise
位于except塊中的 raise,在之前已經手動引發了 ValueError 例外,這里再使用 raise 陳述句時,會再次引發一次
try:
a = input("輸入一個數:")
if(not a.isdigit()): #判斷輸入的是否為數字
raise
except RuntimeError as e:
print("引發例外:",repr(e))
輸入一個數:a
引發例外: RuntimeError('No active exception to reraise',)
在沒有引發過例外的程式使用無參的 raise 陳述句,默認引發的是 RuntimeError 例外
六.例外處理機制的正確使用
不要使用例外處理來代替正常的業務邏輯判斷
例外處理機制的初衷是將不可預期例外的處理代碼和正常的業務邏輯處理代碼分離
不要過度使用例外
過度使用例外
1.把例外和普通錯誤混淆在一起,不撰寫任何錯誤處理代碼,以簡單地引發例外來代替所有的錯誤處理
2.使用例外處理來代替流程控制
例外只應該用于處理非正常的情況,不要使用例外處理來代替正常的流程控制
對于可預知,而且處理方式清楚的錯誤,程式應該提供相應的錯誤處理代碼,而不是將其籠統地稱為例外
不要使用過于龐大的 try 塊
try 塊里的代碼過于龐大,會造成 try 塊中出現例外的可能性大大增加,從而導致分析例外原因的難度也大大增加
在同一個 try 塊后緊跟大量的 except 塊需要分析它們之間的邏輯關系,增加了編程復雜度
正確的做法是把龐大的 try 塊分割成多個可能出現例外的程式段落,并把它們放在單獨的 try 塊中,從而分別捕獲并處理例外
不要忽略捕獲到的例外
己捕獲到例外,應及處理并修復例外
處理例外,對例外進行合適的修復,繞過例外發生的地方繼續運行……程式應該盡量修復例外,使程式能恢復運行
重新引發新例外,把在當前運行環境下能做的事情盡量做完,然后進行例外轉譯,把例外包裝成當前層的例外,重新傳給上層呼叫者
如果當前層不清楚如何處理例外,不要在當前層使用 except 陳述句來捕獲該例外,讓上層呼叫者來負責處理該例外
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/259738.html
標籤:python
