甲居工作室

10.Python 例外 (Exception)

日期:2024/04/09

10.Python 例外 (Exception)

想必到目前為止應該看過許多的錯誤 (Error)訊息。然後就 88 了。此章節主要說明如何處理錯誤狀況,以防程式出錯而導致無法運行甚至發生其他狀況,如檔案毀損。

10.1 常見例外

  • SyntaxError: 語法錯誤時引發。
  • TypeError: 資料型態錯誤時引發。
  • NameError: 當使用未宣告/不存在的變數名或函數名時引發。
  • IndexError: 當序列中沒有這樣的索引(例如列表、元組)時引發。
  • KeyError: 當字典中沒有這樣的鍵時引發。
  • ValueError: 當使用無效參數呼叫函數或方法時引發。
  • IOError: 當發生 I/O 相關的錯誤時引發,例如文件操作。
  • FileNotFoundError: 當打開不存在的檔案時引發。
  • PermissionError: 當權限錯誤時引發。
  • OverflowError: 當算術運算結果太大無法表示時引發。
  • ZeroDivisionError: 當嘗試除以零時引發。

10.2 例外處理(Exception Handling)

可以通過try-except語句來處理例外。 try 區塊中包含可能引發例外的程式碼,except區塊中包含處理例外的程式碼。

所有例外

  1. try:
  2. # 程式碼
  3. pass
  4. except Exception as e:
  5. # 異常處理程式碼
  6. print(f"發生錯誤:{e}")
  • Exception as e:用來捕獲異常狀態並且宣告變數 e,如無需要則可省略。

特定例外

  1. try:
  2. # 程式碼
  3. pass
  4. except SpecificException as e:
  5. # 處理特定異常的程式碼
  6. print(f"發生了 {type(e).__name__} 類型的錯誤:{e}")
  7. except AnotherSpecificException as e:
  8. # 處理另一種特定異常的程式碼
  9. print(f"發生了 {type(e).__name__} 類型的錯誤:{e}")

沒有例外發生

  1. try:
  2. # 可能引發異常的程式碼區塊
  3. result = 10 / 2
  4. except ZeroDivisionError:
  5. print("除以零錯誤發生")
  6. else:
  7. print("沒有發生異常")
  8. print("結果是:", result)

finally

不論例外是否發生,都需要執行某些程式,可以寫在 finally 部份 (部分時候try不會執行,例如程式被強制終止)。

  1. try:
  2. # 可能引發異常的程式碼區塊
  3. f = open("example.txt", "r")
  4. print(f.read())
  5. except FileNotFoundError:
  6. print("檔案未找到")
  7. finally:
  8. # 無論是否發生異常都會執行的程式碼區塊
  9. if 'f' in locals():
  10. f.close() # 確保檔案被關閉
  11. print("執行結束")

10.3 with

預定義的清理動作,某些時候開啟或是使用資源沒有正確的關閉或釋放,該資源就會搶佔著記憶體,資源小時無感但當檔案資源很大時就會影響效率。
通常用於處理文件、資料庫連接、網路連接等需要顯式關閉或清理的資源。

基本語法

  1. with expression as variable:
  2. # 使用資源的程式碼區塊

-expression:是一個返回上下文管理器物件的表達式,通常是一個函式呼叫。
-variable:是可選的,用於將上下文管理器的結果賦值給一個變數,以便在with區塊中使用。

  1. with open("file.txt", "r") as file:
  2. content = file.read()
  3. # 檔案會在with區塊結束時自動關閉
  1. import sqlite3
  2. with sqlite3.connect("example.db") as conn:
  3. pass
  4. # 檔案會在with區塊結束時自動關閉

10.4 引發例外

raise 語法強制引發指定的例外。例如:

  1. raise [Exception [from previous_exception]]

-Exception:是要引發的例外類型,可以是Python內建的例外類型,也可以是自定義的例外類。
-from previous_exception:是可選的,用於在例外鏈接中指定前一個例外。

範例

  • 引發內建例外:
    1. raise ValueError("無效值")
  • 引發自定義例外:
    1. class MyCustomError(Exception):
    2. pass
    3. raise MyCustomError("發生了錯誤")
  • 例外鏈接 (Exception Chaining)
    1. try:
    2. some_function_that_may_raise_exception()
    3. except SomeException as e:
    4. raise AnotherException("發生了錯誤") from e
    在這個例子中,如果some_function_that_may_raise_exception()引發了SomeException,程式將捕獲這個異常,並且重新引發一個新的異常AnotherException,同時將原始的異常作為上下文傳遞給新的異常。

假設你有一個函式從文件中讀取數字並進行除法運算:

  1. def divide_numbers(file_path):
  2. try:
  3. with open(file_path, 'r') as f:
  4. num1 = int(f.readline().strip())
  5. num2 = int(f.readline().strip())
  6. result = num1 / num2
  7. return result
  8. except FileNotFoundError as e:
  9. raise ValueError(f'無法找到文件: {file_path}') from e
  10. except ZeroDivisionError as e:
  11. raise ValueError('除數不能為零') from e
  12. except ValueError as e:
  13. raise ValueError('文件中應包含兩個整數行') from e
  • 如果文件不存在,我們捕獲 FileNotFoundError 並且使用raise from 來建立一個異常鏈,新引發的異常是 ValueError,並且將原始的 FileNotFoundError 作為上下文。
  • 如果除數為零,我們捕獲 ZeroDivisionErro r並且也使用 raise from 建立一個異常鏈,新引發的異常仍然是 ValueError,並且將原始的 ZeroDivisionError 作為上下文。
  • 如果從文件中讀取的數據無法轉換為整數,我們捕獲ValueError 並且同樣使用 raise from 建立一個異常鏈,新引發的異常仍然是 ValueError,並且將原始的 ValueError 作為上下文。

延伸閱讀

11.Python 類別 (Class)

『物件導向程式設計』(Object-oriented programming, OOP) 是一種程式開發的概念,以物件的概念來解決問題。 封裝、繼承、多型為物件導向的三大特性,本章節會略提此概念,…

Read more

12. Python 撰寫準則

想必大家上一章節頭腦都快要炸裂了吧? 本章談論輕鬆一點的事情。

Read more