寫出來的python程式不一定是完全正确的。即使所有邏輯都對了,但是使用者在使用過程的不當,也會導緻程式運作出錯。
本文結構:
常見的異常類型
如何處理異常
如何自定義異常
1. 常見的異常大概有以下的一些:
NameError 嘗試通路一個沒有聲明的變量
如圖,在f() 函數裡,試圖列印一個未被定義的變量yy,當調用f()函數時,将會抛出一個NameError 的異常。

ImportError 無法引入子產品或包;可能路徑不存在
如圖,試圖import 子產品abc12,但是由于abc12 并不是已經被定義了的子產品,是以程式就會抛出ImportError 的異常。
IndentationError 文法錯誤(的子類);代碼沒有正确對齊
如圖1,如果空格鍵和Tab鍵混用,也是會抛出IndentationError 這個文法錯誤。a前用了空格鍵,b前用了tab鍵。
如圖2,就是明顯的沒有對齊了,文法錯誤。
SyntaxError 文法錯誤
IndexError 索引超出序列範圍
如圖,通路清單時,通路了超出索引範圍,就會抛出IndexError 異常
KeyError 請求一個不存在的字典關鍵字
如圖,對字典dic 的通路,使用了一個不存在的key值,就會抛出KeyError異常
IOError 輸入輸出錯誤(比如要讀的檔案不存在)
如圖,用open() 函數嘗試打開一個不存在的檔案,則會抛出IOError異常。但是如果是使用‘w’的方式打開,則不存在的檔案會被建立。
AttributeError 通路未知對象屬性
ValueError 傳給函數的參數類型不正确,比如int()函數傳入字元串函數
如圖,給int()函數傳入一個非數字的字元參數,會抛出ValueError 異常
UnboundLocalError 試圖通路一個還未被設定的局部變量
如圖,變量y 在函數f()外面已經被定義,但是在f()方法裡,它是一個局部變量,y還沒使用,不能直接對其進行指派操作。
但是,在第二個f()函數的定義中,直接print 列印y值,這樣做是不會抛出異常的。
KeyboardInterrupt 鍵盤中斷異常,例如ctrl+c
time.sleep(60) 讓程式休眠60秒,在休眠的過程中,按下ctrl+c,将會終端time.sleep()程序,同時抛出KeyboardInterrupt異常。
2. 如何處理異常
在python中處理異常,需要對異常捕獲,再處理。在python中捕獲異常的代碼大概是這樣的:
如上,在try 裡捕獲異常,如果try_suite 抛出了異常,那麼異常就會被捕獲。
然後except 對捕獲的異常類型進行判斷,如果是想要捕獲的異常類型,則在excepttion_suite 裡做一些處理。
Exception1, Exception2 是Exception 異常類型對象,例如在文章第一部分列出的常見異常類型NameError,IndexError等異常類型。
Argument
else 語句,是當try 沒有捕獲到任何異常時,就會執行no_exception_detected_suite 裡的操作。但是,else 部分并不是必須的,它可以被省略。即使省略了else 部分,甚至将except 部分也省略了,也是可以的。隻要有try 部分就可以對異常進行捕獲了。
finally 語句,無論是否捕獲到異常,異常處理是否被執行了,都一定會将finally部分執行的。是以,通常會将,檔案關閉,資源回收這些操作放在finally部分執行。finally 部分也是可以省略的。
2.1 try...except... 結構
大概的結構:
隻要try_suite 抛出異常,可能會被try 語句捕獲,try 捕獲後,就會将異常交給except 語句處理,如果異常是except 需要判定的Exception 則,執行except_suite 的語句。
例子:
如上圖,try 捕獲到ZeroDivisionError, 通過Except 判定是需要捕獲的,則執行print 輸出代碼塊。
try...except... 可以一次捕獲多個錯誤,可以将異常寫在同一個except 裡,也可以分開在多個except 語句,例如:
如上圖,分别用兩個except 語句處理了try語句捕獲到的ZeroDivisionError 和ValueError 的異常。
如果不知道需要捕獲什麼異常,except 可以什麼異常類型都不帶。例如:
如圖,兩個函數裡的try...except... 語句塊裡的except 都沒有帶具體的Exception類型,是以隻要在try 語句裡捕獲到的任何異常,交給except 語句都會執行except_suite 語句。
當然,除了将多個異常寫在不同的except 語句裡,也可以将多個異常類型寫在一個except 裡,例如:
except 語句除了能捕獲異常類型外,還可以建立異常類型的執行個體,通過異常的執行個體可以 知道有關異常的更多資訊,這對我們跟蹤和處理異常是很有幫助的。異常的執行個體,就是except Exception1,Exception2,...,Argument裡的Argument參數了。Exception1,Exception2等都是異常的類型,如果try 捕獲到的異常,是except 需要判定的,那麼Argument 就會被except 建立為該類型的執行個體。通過執行個體,可以知道更多的異常資訊。例如:
如圖,try 捕獲了一個除零異常,并且建立了一個異常類型的執行個體Argument。
通過列印Argume 的資訊,可以更具體地知道異常的資訊。
2.2 try...except...else...
try...except... 就不用多說了。else類似于if...elsse... 結構的else.當try 沒有捕獲到異常,那麼else 語句中的other_suite 語句将會執行。
如上圖,第一次打開目前路徑存在的檔案,沒有異常抛出,則執行了except 的語句。第二次,嘗試打開一個目前路徑不存在的檔案,則抛出了IOError 的異常。
2.3 finally...子句
finally 語句,無論有沒有異常被捕獲,它都會執行。可以try和finally 配合使用,也可以和except 和else一起配合使用。
例如:
3. 自定義異常
通過自定義異常類,但是這個類需要繼承父類Exception 異常類.
(1) 定義了一個繼承自Exception 的自定義異常類FunError ,類裡的__str__()方法被定義為return 一條資訊,通常作為異常資訊。
(2) 定義一個f() 函數,在函數裡抛出異常,使用關鍵字raise。raise 關鍵字可以用來抛出一個異常。
(3) 是以,如果調用f() 函數,那麼函數就會抛出一個FunError()的異常類。
(4) 當抛出FunError() 異常類時,異常類的執行個體同時被建立,那麼類的__str__() 方法就會被調用,是以,就可以看到異常資訊FunError: I am a func Error.
是以,自定義的FunError 類,也是可以被try 來捕獲的,例如:
如圖,在try 裡調用f() 函數,它會抛出異常,被except 捕獲,将異常類的執行個體e 列印,列印的就是類的__str__() 方法的資訊。