目錄
- 一、bug的簡介
- 二、異常捕獲預處理機制
- 1. 繼承
- 2. 捕獲與預處理
- 3. 嵌套與傳遞
一、bug的簡介
??1947年,世界第一部萬用計算機的進化版——馬克2號(Mark II
)的程序運行發生了異常,計算機程序之母格蕾絲經調試后,發現是一只飛蛾卡在了繼電器觸點中間,于是她把蟲子的尸體
粘在了自己的工作日記上,稱它們為臭蟲(bug
)。
??從此以后,計算機程序或硬件系統中存在的錯誤、故障、缺陷或漏洞,統稱為bug
。調試程序的步驟,也稱為debug
。
??當bug
導致程序跳出運行時,系統會報出對應的異常類型,Python
中常見的異常有:語法格式錯誤SyntaxError
,值類型錯誤ValueError
,空索引錯誤IndexError
,空鍵錯誤KeyError
,未聲明錯誤NameError
,未分配錯誤AttributeError
,除零錯誤ZeroDivisionError
,……
二、異常捕獲預處理機制
1. 繼承
??Python
中,異常也是對象,所有內置的異常類都是從BaseException
類繼承,且都在內建模塊builtins
中定義。在捕獲異常時,匹配的是其對應的異常類及其父類。繼承層次如下:
BaseException # 所有異常的基類+-- SystemExit # 解釋器請求退出+-- KeyboardInterrupt # 用戶中斷執行(Ctrl+C)+-- GeneratorExit # 生成器異常退出+-- Exception # 常規異常的基類+-- StopIteration # 迭代器沒有更多的值+-- StopAsyncIteration # 必須通過異步迭代器對象的__anext__()方法引發以停止迭代+-- ArithmeticError # 算術錯誤異常的基類| +-- FloatingPointError # 浮點計算錯誤| +-- OverflowError # 溢位錯誤(數值太大)| +-- ZeroDivisionError # 除(模)零 +-- AssertionError # 斷言失敗+-- AttributeError # 屬性/方法的引用或賦值失敗+-- BufferError # 緩沖區錯誤+-- EOFError # 當input()函數在未讀取任何數據時達到文件結束條件(EOF)時引發+-- ImportError # 導入模塊/對象失敗| +-- ModuleNotFoundError # 尋找模塊失敗+-- LookupError # 無效鍵/索引時的基類| +-- IndexError # 序列無此索引| +-- KeyError # 映射無此鍵+-- MemoryError # 內存溢出錯誤+-- NameError # 對象未聲明/初始化 | +-- UnboundLocalError # 本地變量未初始化+-- OSError # 操作系統錯誤| +-- BlockingIOError # 操作將阻塞對象(e.g.socket)設置為非阻塞操作| +-- ChildProcessError # 子進程操作失敗| +-- ConnectionError # 連接異常的基類| | +-- BrokenPipeError # 在已關閉的管道寫入| | +-- ConnectionAbortedError # 連接嘗試被對等方中止| | +-- ConnectionRefusedError # 連接嘗試被對等方拒絕| | +-- ConnectionResetError # 連接由對等方重置| +-- FileExistsError # 創建已存在的文件或目錄| +-- FileNotFoundError # 請求不存在的文件或目錄| +-- InterruptedError # 系統調用被輸入信號中斷| +-- IsADirectoryError # 在目錄上請求文件操作(e.g.os.remove())| +-- NotADirectoryError # 在非目錄對象上請求目錄操作(e.g.os.listdir())| +-- PermissionError # 操作權限不足| +-- ProcessLookupError # 給定進程不存在| +-- TimeoutError # 系統函數在系統級別超時+-- ReferenceError # weakref.proxy()函數創建的弱引用試圖訪問已經垃圾回收了的對象+-- RuntimeError # 未定義類錯誤| +-- NotImplementedError # 在用戶定義的基類中,抽象方法要求派生類重寫該方法或者正在開發的類指示仍需添加實際實現| +-- RecursionError # 超出最大遞歸深度+-- SyntaxError # 語法格式錯誤| +-- IndentationError # 縮進錯誤| +-- TabError # Tab和空格混用+-- SystemError # 解釋器內部錯誤+-- TypeError # 操作或函數應用于不適當類型的對象+-- ValueError # 操作或函數接收到具有正確類型但值不合適的參數| +-- UnicodeError # Unicode錯誤| +-- UnicodeDecodeError # Unicode解碼錯誤| +-- UnicodeEncodeError # Unicode編碼錯誤| +-- UnicodeTranslateError # Unicode轉碼錯誤+-- Warning # 警告的基類+-- DeprecationWarning # 已棄用功能警告+-- PendingDeprecationWarning # 不推薦使用功能警告+-- RuntimeWarning # 可疑行為警告+-- SyntaxWarning # 可疑語法警告+-- UserWarning # 用戶代碼生成警告+-- FutureWarning # 可能錯誤警告+-- ImportWarning # 模塊導入警告+-- UnicodeWarning # Unicode警告+-- BytesWarning # 與bytes和bytearray相關的警告+-- ResourceWarning # 資源使用警告(被默認警告過濾器忽略)
?
2. 捕獲與預處理
??try...except...
語句用于捕獲并處理異常,其機制為:
??try
下的代碼塊運行時,如果發生異常,不跳出程序,從上到下依次檢索except
子句,并只執行第一個匹配該異常的except
子句。
try:n = 1/0 # 發生錯誤,轉到except語句print(n) # 不執行
except SyntaxError or ValueError: # 異常類型錯誤print('Error') # 不執行
except ZeroDivisionError: # 異常類型正確print('ZeroDivisionError') # 打印錯誤
??若需要對其他所有錯誤有統一的應對行為,可不設置異常類型,或者將except
子句匹配的異常類型設為BaseException
。此外,也可以通過as
將匹配的異常賦給變量,以便于查詢異常發生的具體原因,并防止 “異常字句過于寬泛” 的警告。
try:n = mprint(n)
# 一般是最后一個
except BaseException as E: # except:(必須是最后一個)print(E) # name 'm' is not defined
?
3. 嵌套與傳遞
??try
塊可以在許多結構中嵌套,包括自身、循環結構和函數體等等。在多層嵌套中,異常的傳遞機制為:
??若異常未被內層的except
子句捕獲,則將被遞交到外層的try
塊,直至異常被處理。如果都不處理該異常,就終止其所在線程。
??在此基礎上,還有追加子句else
和finally
。當沒有匹配的except
子句時,異常就會由else
子句捕獲,并執行else
子塊。而不論try
塊中是否發生異常,finally
子句最終都會被執行,并釋放本層try
塊的資源。
# 載入精確計算庫
from decimal import Decimal
# 初始化循環條件
flag = 1while flag:# 運算器主體try:# 功能說明print('保留三位小數的除法運算器'.center(20))# 輸入參數n = input('被除數:')m = input('除數:')# 計算結果result = '商:{:.3f}'.format(float(Decimal(n) / Decimal(m)))# 除零錯誤except ZeroDivisionError:print('division by zero')# 輸入類型錯誤except ArithmeticError:print('invalid operand type(s)')# 無異常,輸出結果else:print(result)# 循環控制finally:try:flag = int(input('輸入1繼續,輸入0退出:'))except BaseException:flag = 0
??以上實例代碼實現了一個保留三位小數的除法運算器。其中,finally
子句中嵌套了一個內層try
塊。另外,由于調用了第三方庫,大部分發生的相似錯誤并不屬于常規異常子類,可以考慮向其父類追溯。
??特別的,循環結構或函數體中的內層try
塊中的finally
子句里,若含有break
或return
語句,那么異常不再發生傳遞,并且其所在進程將被終止。