Python編程中的錯誤與異常

到目前爲止,我們還沒有提到錯誤消息,但是如果你已經嘗試過那些例子,你可能已經看過了一些錯誤消息。 目前(至少)有兩種可區分的錯誤:語法錯誤 和 異常。
語法錯誤

語法錯誤又稱解析錯誤,可能是你在學習Python 時最容易遇到的錯誤:
Python編程中的錯誤與異常

解析器會輸出出現語法錯誤的那一行,並顯示一個“箭頭”,指向這行裏面檢測到第一個錯誤。 錯誤是由箭頭指示的位置 上面 的 token 引起的(或者至少是在這裏被檢測出的):在示例中,在 print() 這個函數中檢測到了錯誤,因爲在它前面少了個冒號 (':') 。文件名和行號也會被輸出,以便輸入來自腳本文件時你能知道去哪檢查。
異常

即使語句或表達式在語法上是正確的,但在嘗試執行時,它仍可能會引發錯誤。 在執行時檢測到的錯誤被稱爲異常,異常不一定會導致嚴重後果:你將很快學會如何在Python程序中處理它們。 但是,大多數異常並不會被程序處理,此時會顯示如下所示的錯誤信息:
Python編程中的錯誤與異常

錯誤信息的最後一行告訴我們程序遇到了什麼類型的錯誤。異常有不同的類型,而其類型名稱將會作爲錯誤信息的一部分中打印出來:上述示例中的異常類型依次是:ZeroDivisionError, NameError 和 TypeError。作爲異常類型打印的字符串是發生的內置異常的名稱。對於所有內置異常都是如此,但對於用戶定義的異常則不一定如此(雖然這是一個有用的規範)。標準的異常類型是內置的標識符(而不是保留關鍵字)。

這一行的剩下的部分根據異常類型及其原因提供詳細信息。

錯誤信息的前一部分以堆棧回溯的形式顯示發生異常時的上下文。通常它包含列出源代碼行的堆棧回溯;但是它不會顯示從標準輸入中讀取的行。

內置異常 列出了內置異常和它們的含義。
處理異常

可以編寫處理所選異常的程序。請看下面的例子,它會要求用戶一直輸入,直到輸入的是一個有效的整數,但允許用戶中斷程序(使用 Control-C 或操作系統支持的其他操作);請注意用戶引起的中斷可以通過引發 KeyboardInterrupt 異常來指示。:
Python編程中的錯誤與異常

try 語句的工作原理如下。

首先,執行 try 子句 (try 和 except 關鍵字之間的(多行)語句)。
如果沒有異常發生,則跳過 except 子句 並完成 try 語句的執行。
如果在執行try 子句時發生了異常,則跳過該子句中剩下的部分。然後,如果異常的類型和 except 關鍵字後面的異常匹配,則執行 except 子句 ,然後繼續執行 try 語句之後的代碼。
如果發生的異常和 except 子句中指定的異常不匹配,則將其傳遞到外部的 try 語句中;如果沒有找到處理程序,則它是一個 未處理異常,執行將停止並顯示如上所示的消息。

一個 try 語句可能有多個 except 子句,以指定不同異常的處理程序。 最多會執行一個處理程序。 處理程序只處理相應的 try 子句中發生的異常,而不處理同一 try 語句內其他處理程序中的異常。 一個 except 子句可以將多個異常命名爲帶括號的元組,例如:

... except (RuntimeError, TypeError, NameError):
... pass

如果發生的異常和 except 子句中的類是同一個類或者是它的基類,則異常和except子句中的類是兼容的(但反過來則不成立 --- 列出派生類的except 子句與基類兼容)。例如,下面的代碼將依次打印 B, C, D
Python編程中的錯誤與異常

請注意如果 except 子句被顛倒(把 except B 放到第一個),它將打印 B,B,B --- 即第一個匹配的 except 子句被觸發。

最後的 except 子句可以省略異常名,以用作通配符。但請謹慎使用,因爲以這種方式很容易掩蓋真正的編程錯誤!它還可用於打印錯誤消息,然後重新引發異常(同樣允許調用者處理異常):
Python編程中的錯誤與異常

try ... except 語句有一個可選的 else 子句,在使用時必須放在所有的 except 子句後面。對於在try 子句不引發異常時必須執行的代碼來說很有用。例如:
Python編程中的錯誤與異常

使用 else 子句比向 try 子句添加額外的代碼要好,因爲它避免了意外捕獲由 try ... except 語句保護的代碼未引發的異常。

發生異常時,它可能具有關聯值,也稱爲異常 參數 。參數的存在和類型取決於異常類型。

except 子句可以在異常名稱後面指定一個變量。這個變量和一個異常實例綁定,它的參數存儲在 instance.args 中。爲了方便起見,異常實例定義了 str() ,因此可以直接打印參數而無需引用 .args 。也可以在拋出之前首先實例化異常,並根據需要向其添加任何屬性。:
Python編程中的錯誤與異常

如果異常有參數,則它們將作爲未處理異常的消息的最後一部分('詳細信息')打印。

異常處理程序不僅處理 try 子句中遇到的異常,還處理 try 子句中調用(即使是間接地)的函數內部發生的異常。例如:
Python編程中的錯誤與異常

拋出異常

raise 語句允許程序員強制發生指定的異常。例如:

raise NameError('HiThere')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: HiThere

raise 唯一的參數就是要拋出的異常。這個參數必須是一個異常實例或者是一個異常類(派生自 Exception 的類)。如果傳遞的是一個異常類,它將通過調用沒有參數的構造函數來隱式實例化:

raise ValueError # shorthand for 'raise ValueError()'

如果你需要確定是否引發了異常但不打算處理它,則可以使用更簡單的 raise 語句形式重新引發異常
Python編程中的錯誤與異常

用戶自定義異常

程序可以通過創建新的異常類來命名它們自己的異常(有關Python 類的更多信息,請參閱 類)。異常通常應該直接或間接地從 Exception 類派生。

可以定義異常類,它可以執行任何其他類可以執行的任何操作,但通常保持簡單,通常只提供許多屬性,這些屬性允許處理程序爲異常提取有關錯誤的信息。在創建可能引發多個不同錯誤的模塊時,通常的做法是爲該模塊定義的異常創建基類,併爲不同錯誤條件創建特定異常類的子類:
Python編程中的錯誤與異常

大多數異常都定義爲名稱以“Error”結尾,類似於標準異常的命名。

許多標準模塊定義了它們自己的異常,以報告它們定義的函數中可能出現的錯誤。有關類的更多信息,請參見類 類。
定義清理操作

try 語句有另一個可選子句,用於定義必須在所有情況下執行的清理操作。例如:
Python編程中的錯誤與異常

正如你所看到的,finally 子句在任何情況下都會被執行。 兩個字符串相除所引發的 TypeError 不會由 except 子句處理,因此會在 finally 子句執行後被重新引發。

在實際應用程序中,finally 子句對於釋放外部資源(例如文件或者網絡連接)非常有用,無論是否成功使用資源。
預定義的清理操作

某些對象定義了在不再需要該對象時要執行的標準清理操作,無論使用該對象的操作是成功還是失敗。請查看下面的示例,它嘗試打開一個文件並把其內容打印到屏幕上。:

for line in open("myfile.txt"):
print(line, end="")

這個代碼的問題在於,它在這部分代碼執行完後,會使文件在一段不確定的時間內處於打開狀態。這在簡單腳本中不是問題,但對於較大的應用程序來說可能是個問題。 with 語句允許像文件這樣的對象能夠以一種確保它們得到及時和正確的清理的方式使用。:

with open("myfile.txt") as f:
for line in f:
print(line, end="")

執行完語句後,即使在處理行時遇到問題,文件 f 也始終會被關閉。和文件一樣,提供預定義清理操作的對象將在其文檔中指出這一點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章