例外處理#
程式執行時若發生錯誤,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}")
|