例外處理#

程式執行時若發生錯誤,Python 會「拋出(raise)」一個 例外(Exception) ,若未妥善處理,程式就會終止並顯示錯誤訊息:

1
2
3
4
5
6
7
8
9
print(10 / 0)
# ZeroDivisionError: division by zero

int("hello")
# ValueError: invalid literal for int() with base 10: 'hello'

numbers = [1, 2, 3]
print(numbers[10])
# IndexError: list index out of range

try / except 基本用法#

try 包住可能出錯的程式碼,except 處理例外:

1
2
3
4
5
6
try:
    result = 10 / 0
except ZeroDivisionError:
    print("錯誤:不能除以零")

print("程式繼續執行")

捕捉多種例外#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        print("除數不能為零")
    except TypeError:
        print("參數必須是數字")
    return None

safe_divide(10, 0)       # 除數不能為零
safe_divide(10, "two")   # 參數必須是數字

用一個 except 捕捉多種例外#

1
2
3
4
try:
    value = int(input("請輸入數字:"))
except (ValueError, TypeError):
    print("輸入格式不正確")

捕捉所有例外#

1
2
3
4
5
6
try:
    # 某些不確定的操作
    risky_operation()
except Exception as e:
    print(f"發生錯誤:{e}")
    print(f"錯誤類型:{type(e).__name__}")

else 與 finally#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
try:
    result = 10 / 2
except ZeroDivisionError:
    print("除以零了")
else:
    # try 區塊沒有發生例外時執行
    print(f"計算成功:{result}")
finally:
    # 無論是否發生例外,一定會執行
    print("計算完畢")

finally 的典型用途 :確保資源被釋放(如關閉檔案、資料庫連線):

1
2
3
4
5
6
7
8
9
file = None
try:
    file = open("data.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("找不到檔案")
finally:
    if file:
        file.close()  # 無論如何都要關閉

常見例外類型#

例外類型發生時機
ValueError值的格式不正確(如 int("abc")
TypeError型別不對(如 "2" + 2
ZeroDivisionError除以零
IndexError串列索引超出範圍
KeyError字典中找不到指定的鍵
AttributeError物件沒有該屬性或方法
FileNotFoundError找不到指定的檔案
NameError使用未定義的變數
ImportError無法匯入模組
StopIteration迭代器沒有更多元素

取得例外資訊#

1
2
3
4
5
try:
    result = int("abc")
except ValueError as e:
    print(f"例外訊息:{e}")
    print(f"例外型別:{type(e).__name__}")

主動拋出例外(raise)#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def set_age(age):
    if not isinstance(age, int):
        raise TypeError("年齡必須是整數")
    if age < 0 or age > 150:
        raise ValueError(f"年齡 {age} 超出合理範圍(0-150)")
    print(f"年齡設定為 {age}")

try:
    set_age(-5)
except ValueError as e:
    print(f"錯誤:{e}")

try:
    set_age("twenty")
except TypeError as e:
    print(f"錯誤:{e}")

自訂例外類別#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class InsufficientFundsError(Exception):
    """餘額不足例外"""
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        super().__init__(f"餘額不足:現有 {balance},需要 {amount}")


class BankAccount:
    def __init__(self, balance):
        self.balance = balance

    def withdraw(self, amount):
        if amount > self.balance:
            raise InsufficientFundsError(self.balance, amount)
        self.balance -= amount
        print(f"提款 {amount},剩餘 {self.balance}")


account = BankAccount(1000)

try:
    account.withdraw(500)   # 提款 500,剩餘 500
    account.withdraw(800)   # 超過餘額
except InsufficientFundsError as e:
    print(f"交易失敗:{e}")

實戰範例#

安全的資料轉換#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def safe_int(value, default=0):
    """嘗試將值轉為整數,失敗時回傳預設值"""
    try:
        return int(value)
    except (ValueError, TypeError):
        return default

print(safe_int("42"))      # 42
print(safe_int("abc"))     # 0
print(safe_int(None))      # 0
print(safe_int("3.7"))     # 0(int 不接受小數字串)

重試機制#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import random

def fetch_data():
    """模擬可能失敗的操作"""
    if random.random() < 0.7:  # 70% 機率失敗
        raise ConnectionError("連線失敗")
    return "資料取得成功"

max_retries = 3
for attempt in range(1, max_retries + 1):
    try:
        result = fetch_data()
        print(result)
        break
    except ConnectionError as e:
        print(f"第 {attempt} 次嘗試失敗:{e}")
        if attempt == max_retries:
            print("已達最大重試次數,放棄")

驗證使用者輸入#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def get_positive_number(prompt):
    """持續要求使用者輸入,直到輸入合法的正整數"""
    while True:
        try:
            value = int(input(prompt))
            if value <= 0:
                raise ValueError("必須是正整數")
            return value
        except ValueError as e:
            print(f"輸入錯誤:{e},請重新輸入。")

# n = get_positive_number("請輸入正整數:")
# print(f"你輸入了:{n}")