14. 異常處理

星光映梦發表於2024-10-08

一、什麼是異常

  程式在執行過程之中,不可避免的出現一些錯誤,比如:使用了沒有賦值的變數、使用了不存在的索引、除 0 等等。這些錯誤在程式中,我們稱之為異常。程式執行過程中,一旦出現異常將會導致程式立即終止,異常以後的程式碼全部都不會執行。

二、異常的傳播

  當在函式中出現異常時,如果在函式中對異常進行了處理,則異常不會繼續傳播。如果函式中不會對異常進行處理,,則異常會繼續向函式呼叫處傳播。如果函式呼叫處處理了異常,則不再傳播,如果沒有則繼續向呼叫處傳播。直到傳遞到全域性作用域(主模組),如果依然沒有處理,則程式終止,並顯示異常資訊。

  當程式執行過程中出現異常以後,所有的異常資訊會被儲存在一個專門的異常物件,而異常傳播時,實際上就是異常物件拋給了呼叫處。比如,ZeroDivisionError 類的物件專門用來表示除 0 的異常,NameError 類的物件專門用來處理變數錯誤的異常。在 Python 中提供了多個異常物件。

def fun1():
    a  = 10 / 0

def fun2():
    fun1()

def fun3():
    fun2()

fun3()

三、異常處理機制

  程式執行時出現異常,目的並不是讓程式直接終止。Python 是希望在出現異常時,我們可以編寫程式碼來對異常進行處理。遇到異常時,Python 中有兩種處理機制。

3.1、try...except機制

  在 Python 中,提供 try...catch 語句捕獲並處理異常。在使用時,把可能產生異常的程式碼放在 try 子句中,把處理結果放在 except 子句中。這樣,當 try 子句中的程式碼塊出現異常時,就會執行 except 語句塊中的內容。如果 try 語句塊中程式碼沒有異常,那麼 except 語句塊不會執行。這樣我們就可以透過程式碼來處理異常,避免因為一個異常導致整個程式的終止。

  我們還可以在原有的基礎上在新增一個 else 子句,用於指定當 try 語句塊中沒有出現異常時要執行的語句塊。該語句塊中的內容當 try 語句中發現異常時,將不被執行。

  我們還可以在新增一個 finallty 子句。該子句中的程式碼無論是否發生異常都會被執行。

try:
    程式碼塊(可能出現異常的程式碼)
except [異常名 [as 異常物件名]]:
    程式碼塊(出現錯誤以後的處理方式)
[else:
    程式碼塊(沒出現時要執行的語句)]
[finally:
    程式碼塊(無論是否出現異常,該子句都會執行)]

  如果 except 後面不跟任何內容,則此時它會捕獲所有的異常。

print("異常處理之前")

try:
    # try中放置的是可能錯誤的程式碼
    print(10/0)
# 如果except後不跟任何內容,此時它會獲取所有的錯誤
except:
    # except中放置的是出現以後的處理程式碼
    print("出現異常")
# 這是一個可選的結構,表示沒有出現異常要執行的程式碼
else:
    print("程式正常異常,沒有錯誤")
# 這是一個可選結構,表示無論是否出現異常,最後都要執行的語句
finally:
    print("無論是否出現異常,該子句都會執行")

print("異常處理之後")

  如果 except 後面跟者異常型別,那麼該型別對應的異常。我們還可以在在異常類後面跟著一個 as xxx,此時 xxx 就是異常物件。

try:
    # try中放置的是可能錯誤的程式碼
    print(10/0)
    print(a)
    1 + "hello"
# 如果except後不跟著異常的型別,那麼它只會捕獲該型別對應的異常
# 可以在異常類後面跟著一個 as xxx,此時xxx就是異常物件
except NameError as e:
    # except中放置的是出現以後的處理程式碼
    print("出現NameError")
    print(e)
except ZeroDivisionError as e:
    # except中放置的是出現以後的處理程式碼
    print("出現ZeroDivisionError")
    print(e)
# Exception 是所有異常類的父類
# 如果except後跟的是 Exception,它會捕獲所有的異常
except Exception as e:
    print("未知異常")
    print(e)

在使用 try...except語句捕獲異常時,如果在 except 後面不指定異常名稱,則表示捕獲全部異常;

在使用 try...except語句捕獲異常時,當程式出現異常並處理完後,程式繼續執行;

我們可以在 except 語句後面使用一對小括號將可能出現的異常名稱括起來,多個異常之間使用逗號分隔。

3.2、raise機制

  如果某個函式或方法可能會產生異常,但不想在當前函式或方法中處理這個異常,則可以使用 raise 語句在函式或方法中丟擲異常。

raise [異常型別名[異常描述]]

  如果異常型別名為可選引數,它用於指定丟擲的異常名稱以及異常資訊的相關描述。如果省略,就會把當前的錯誤原樣丟擲。異常描述也可以省略,如果省略,則在丟擲異常時,不附帶任何描述資訊。

def div(a,b):
    if b == 0:
        # raise用於向外部丟擲異常
        # 後面可以根一個異常類,或異常類的物件
        raise ZeroDivisionError("除0異常")
    return a/b

print(div(10,0))

四、自定義異常類

  我們也可以自定義異常類,只需要建立一個類繼承 Exception 類即可。

class MyException(Exception):
    pass