Python Exception 異常處理​

Python Exception 異常處理 ​在編寫程式時,無論多麼小心,錯誤總是難免會發生。一個穩健 (robust) 的應用程式不僅能完成預期功能,更能在遇到非預期情況時優雅地處理,而不是直接崩潰。這篇文章將帶你深入了解 Python 的異常處理 (Exception Handling) 機制,從基礎概念到進階應用,讓你寫出更可靠的程式碼。

🤔 Error (錯誤) 和 Exception (例外) 有什麼分別? ​Python 沒有 Error 類別,只有 Exception 類別。 但他們把不同的 Exception 都稱為 XxxError,實際上也繼承自 Exception / BaseException,所謂 Error 只是一種稱呼,錯誤其實就是例外。

在 Python 中,所有問題可以簡單歸納為以下兩點:

語法錯誤: 語法有問題,就是 SyntaxError,不能執行例外: 語法沒問題,但執行時出錯,就是一般的 Exception(稱為 XxxError)那麼語法錯誤跟例外的區別在哪裡? 發生時間 和 處理方式。

語法錯誤 (SyntaxError) ​發生時間: 在程式執行 之前 的「解析階段 (parsing phase)」。後果: 由於語法不正確,Python 直譯器無法理解你的程式碼,因此程式根本無法開始執行。處理方式: 你不能使用 try...except 來捕捉 SyntaxError,因為在 try 區塊執行之前,程式就已經因語法錯誤而失敗了。你唯一能做的就是修正程式碼。例如,如果你在 if 語句後面忘記加上冒號 ::

python# 缺少冒號,會導致 SyntaxError

age = 18

if age >= 18

print("You are an adult.")執行這段程式碼會立即得到錯誤訊息,因為它的語法不正確,程式連執行的機會都沒有。

File "", line 2

if age >= 18

^

SyntaxError: invalid syntax例外 (Exception) 和 執行期錯誤 (Runtime Error) ​發生時間: 在程式碼執行期間 (at runtime)。後果: 如果未被處理,會立即中斷程式的執行,導致應用程式崩潰 (crash),並在控制台打印出錯誤的追蹤訊息 (traceback)。處理方式: 這些正是 try...except 結構設計用來捕捉和處理的對象,讓你可以優雅地應對問題,避免程式終止。在 Python 中,所有執行時引發的錯誤都是以「例外 (Exception)」的形式來表示和處理的。換句話說,雖然稱為 Error,實際上就是 Exception。

一個常見的例子是除以零:

pythonresult = 10 / 0

print("這句話永遠不會被執行")這段程式碼的語法完全正確,但當它嘗試計算 10 / 0 時,會觸發一個 ZeroDivisionError 例外。這個例外就是一種執行期錯誤,它會導致程式崩潰。

Traceback (most recent call last):

File "", line 1, in

ZeroDivisionError: division by zero除了像 ZeroDivisionError 或 ValueError 這樣非常具體的例外,Python 還提供了一個更通用的例外類別:RuntimeError。 根據官方文件的定義,RuntimeError 是在「偵測到一個不屬於任何其他特定類別的錯誤時」所引發的。它附帶的值是一個字串,用來說明具體出了什麼問題。這使得它成為一個「兜底」的例外,用於處理那些沒有更精確分類的執行期問題。

總結一下關鍵點:

特性語法錯誤 (SyntaxError)執行期例外 (e.g., ValueError)本質繼承自 Exception繼承自 Exception發生時間程式執行前 (解析階段)程式執行中 (執行期)能否用 try...except 捕捉?否 (程式無法啟動)是解決方法修正程式碼語法使用 try...except 處理警告 (Warning) 不是錯誤 ​還有一種情況是「警告 (Warning)」。警告是用來提醒開發者程式碼中可能存在的問題,例如使用了即將被淘汰 (deprecated) 的功能。與例外不同,警告不會中斷程式的執行,它僅僅是個提示訊息。

🥅 捕捉例外:try...except 結構 ​當你預見某段程式碼可能會引發例外時,你可以使用 try...except 區塊來「捕捉」它,避免程式崩潰。

基本用法 ​將可能出錯的程式碼放在 try 區塊中,並在 except 區塊中定義處理該例外的方式。

pythontry:

num = int(input("請輸入一個數字: "))

result = 100 / num

print(f"100 除以 {num} 的結果是 {result}")

except ZeroDivisionError:

print("錯誤:你不能輸入 0!")

except ValueError:

print("錯誤:請輸入一個有效的數字!")

print("程式繼續執行...")在這個例子中:

如果用戶輸入 0,ZeroDivisionError 會被捕捉,並打印相應的錯誤訊息。如果用戶輸入 abc,ValueError 會被捕捉,因為 int() 無法轉換非數字字串。程式在處理完例外後會繼續執行,打印出「程式繼續執行...」。else 和 finally 子句 ​try...except 結構還有兩個可選的子句:else 和 finally。

else: 如果 try 區塊中 沒有發生 任何例外,else 區塊的程式碼將會被執行。finally: 無論是否發生例外,finally 區塊的程式碼 總會 被執行。這非常適合用來執行清理工作,例如關閉檔案或網絡連接。pythontry:

file = open("data.txt", "r")

content = file.read()

except FileNotFoundError:

print("錯誤:找不到 data.txt 檔案。")

else:

print("檔案內容讀取成功:")

print(content)

finally:

# 確保無論成功或失敗,都會嘗試關閉檔案

# 檢查 file 是否已成功定義,避免在 open() 失敗時引發 NameError

if 'file' in locals() and not file.closed:

file.close()

print("檔案已關閉。")💡 如何處理例外? ​捕捉到例外後,有幾種處理方式。

靜默處理:pass ​有時你可能想完全忽略某個例外。你可以在 except 區塊中使用 pass 關鍵字。

pythonimport os

try:

# 嘗試刪除一個可能不存在的檔案

os.remove("temp_file.tmp")

except FileNotFoundError:

# 如果檔案不存在,就什麼都不做

pass⚠️ 注意:過度使用 pass 是一個壞習慣。它會讓錯誤被悄無聲息地隱藏起來,導致除錯變得極為困難。只在你非常確定忽略該例外是安全且正確的行為時才使用它。

專業的處理方式:使用日誌 (Logging) ​一個比 pass 更好的做法是記錄錯誤,而不是完全忽略它。這樣既不會打擾用戶,也為開發者留下了追蹤問題的線索。Python 內建的 logging 模組是完成這項任務的絕佳工具。

pythonimport logging

# 基本設定:將日誌寫入檔案,記錄等級為 ERROR 或以上

logging.basicConfig(filename='app.log',

level=logging.ERROR,

format='%(asctime)s - %(levelname)s - %(message)s')

try:

result = 10 / 0

except ZeroDivisionError:

# 記錄詳細的例外資訊到日誌檔案中

logging.exception("發生 ZeroDivisionError")

print("發生了一個內部錯誤,已記錄到日誌中。")執行後,用戶只會看到一條友好的提示,而 app.log 檔案中會包含完整的錯誤堆疊追蹤 (stack trace),方便開發者分析問題。

log// app.log 檔案內容

2025-06-10 14:30:00,123 - ERROR - 發生 ZeroDivisionError

Traceback (most recent call last):

File "your_script.py", line 11, in

result = 10 / 0

ZeroDivisionError: division by zero✨ 自訂與引發例外 ​除了處理內建的例外,你還可以定義自己的例外類別,並在適當的時候手動引發它們。

引發例外:raise ​使用 raise 關鍵字可以手動觸發一個例外。這在函式接收到無效參數時特別有用。

pythondef set_age(age):

if not isinstance(age, int) or age < 0:

# 引發一個內建的 ValueError

raise ValueError("年齡必須是一個非負整數。")

print(f"年齡已設定為 {age}")

try:

set_age(-5)

except ValueError as e:

print(f"設定失敗:{e}")建立自訂例外 ​當內建的例外無法精確描述你的應用程式特有的錯誤時,建立自訂例外是個好主意。自訂例外應繼承自內建的 Exception 類別。

python# 1. 定義一個自訂例外類別

class InsufficientBalanceError(Exception):

"""當帳戶餘額不足時引發的例外。"""

pass

# 2. 在程式碼中使用它

def withdraw(balance, amount):

if amount > balance:

# 3. 引發自訂例外

raise InsufficientBalanceError(f"餘額不足,目前餘額 ${balance},嘗試提取 ${amount}")

return balance - amount

# 4. 捕捉自訂例外

my_balance = 500

try:

new_balance = withdraw(my_balance, 1000)

except InsufficientBalanceError as e:

print(f"提款失敗:{e}")建立自訂例外可以讓你的錯誤處理邏輯更加清晰和具體,提高程式碼的可讀性和可維護性。

📝 結論 ​異常處理是現代程式設計中不可或缺的一環。掌握 Python 的異常處理機制,能讓你:

避免程式崩潰:透過 try...except 捕捉潛在例外。編寫穩健的程式碼:使用 finally 確保資源總能被釋放。提供更好的用戶體驗:向用戶顯示友好的錯誤訊息,而不是混亂的堆疊追蹤。簡化除錯過程:使用 logging 記錄詳細的錯誤資訊。提升程式碼的表達力:透過自訂例外來處理特定的應用程式邏輯問題。花時間為你的程式碼加上適當的異常處理,是一項非常值得的投資,它將讓你的應用程式變得更加專業和可靠。

📚 參考資料 ​Python Official Documentation - Errors and ExceptionsPython Official Documentation - Built-in Exceptions (and their hierarchy)Python Official Documentation - logging — Logging facility for Python