寫程式的時候,你一定遇過這種場景:
輸入一個字串結果報錯、除以零整個程式崩潰、讀檔案時跳出紅字!!
整個畫面被滿滿的 Traceback 洗臉.....
這些情況,其實不是「壞掉」,而是程式在對你喊話:
「嘿!我遇到狀況啦,你想怎麼辦?」
這種狀況叫做 例外(Exception)。
例外處理就像程式的「安全氣囊」,
當錯誤發生時,能幫助程式不至於整個爆炸。
今天,我們要搞懂兩個層次:
學完這一章,你會發現——
錯誤不再是程式的敵人,而是可預測、可處理的事件。
例外(Exception)是程式執行時發生的錯誤。
不同於「語法錯誤(Syntax Error)」那種「一開始就不能跑」的錯,
例外是「能執行,但在執行過程中出現問題」!
例外名稱 | 說明 | 範例 |
---|---|---|
ZeroDivisionError |
除以零 | 10 / 0 |
ValueError |
型態錯誤 | int('abc') |
IndexError |
索引超出範圍 | [1,2,3][5] |
KeyError |
字典中找不到 key | mydict['abc'] |
TypeError |
型別不相容 | "3" + 2 |
FileNotFoundError |
找不到檔案 | open('abc.txt') |
NameError |
使用未宣告變數 | print(a) |
例外處理的重點是:「接住錯誤、處理錯誤、不要讓程式崩潰」。
在 Python 世界裡,有兩種思維:
名稱 | 全名 | 思維方式 | 範例 |
---|---|---|---|
EAFP | Easier to Ask Forgiveness than Permission | 直接嘗試、錯了再說抱歉(推薦風格) | 用 try/except 先試後補 |
LBYL | Look Before You Leap | 先檢查能不能做,再執行 | 先 if os.path.exists() 再開檔案 |
try:
with open("data.txt") as f:
content = f.read()
except FileNotFoundError:
print("檔案不存在,建立新的 data.txt 檔案。")
EAFP 優點:程式更簡潔、可避免 race condition(檢查與操作之間狀態改變)
if os.path.exists("data.txt"):
f = open("data.txt")
try:
#受例外機制保護的程式區塊
#區塊內若有例外狀況會停止繼續向下執行
#先跳到except區塊
except :
# 若發生特定例外,執行這裡
範例:
num1 = 10
num2 = 0
nums = [1, 3, 5, 7, 9,11]
try:
print("進入到try區塊")
print(num1 * num2)
print(num1 / (num2))
print(nums[100])
except:
print("產生例外")
print('程式結束')
輸出:
try:
#受例外機制保護的程式區塊
except 例外類別一 :
# try區塊產生例外類別一時,執行這裡
except 例外類別二 :
# try區塊產生例外類別二時,執行這裡
範例:
num1 = 10
num2 = 0
nums = [1,3,5,7,9]
try:
print(num1/num2)
print(num1*num3)
print(nums[100])
except ZeroDivisionError:
print('Error發生,除以0')
except NameError:
print('Error發生,使用沒有宣告過的變數')
輸出:
輸出:
範例:
num1 = 10
num2 = 0
nums = [1,3,5,7,9]
try:
#print(num1/num2)
#print(num1*num3)
print(nums[100])
except ZeroDivisionError:
print('Error發生,除以0')
except NameError:
print('Error發生,使用沒有宣告過的變數')
except:
print('Error發生')
輸出:
else 區塊只有在「沒有發生例外」時執行。
try:
#受例外機制保護的程式區塊
#區塊內若有例外狀況會停止繼續向下執行
else:
#try區塊沒有產生例外時會執行else區塊
num1 = 10
num2 = 0
nums = [1,3,5,7,9]
try:
print(num1/(num2+1))
print(num1*num2)
print(nums[4])
except:
print('Error發生')
else:
print('Error沒發生')
輸出:
finally 無論是否發生例外都會執行,常用於關閉檔案、釋放資源等動作。
try:
#受例外機制保護的程式區塊
#區塊內若有例外狀況會停止繼續向下執行
finally:
#不管try區塊如何,有沒有例外都會執行finally區塊
範例:
num1 = 10
num2 = 0
nums = [1,3,5,7,9]
try:
print(num1/num2)
print(num1*num3)
print(nums[100])
except ZeroDivisionError:
print('Error發生,除以0')
except NameError:
print('Error發生,使用沒有宣告過的變數')
except IndexError:
print('Error發生,索引值超出範圍')
except:
print('Error發生')
finally:
print('結束')
輸出:
範例:
num1 = 10
num2 = 0
nums = [1,3,5,7,9]
try:
print(num1/(num2+1))
print(num1*num2)
print(nums[4])
except:
print('Error發生')
else:
print('Error沒發生')
finally:
print('結束')
輸出:
try:
# 嘗試執行的程式
except ExceptionType:
# 若發生特定例外,執行這裡
else:
# 若沒出錯,執行這裡
finally:
# 無論如何都會執行(收尾動作)
如果你想知道「錯誤的詳細內容」,可以用 as e 取得例外物件。
try:
x = int(input("請輸入數字:"))
y = 10 / x
except ValueError as e:
print("錯誤型態:", type(e))
print("錯誤內容:", str(e))
輸出:
例外會「一路往上傳遞」,直到被捕捉或程式崩潰。
範例:
def a():
b()
def b():
c()
def c():
raise ValueError("C出錯啦!")
try:
a()
except Exception as e:
print("捕捉到例外:", e)
輸出:
說明:
c()發生錯誤 → 傳到b()→ 傳到a()→ 最後被try捕捉。
這個過程叫例外傳遞(Exception Propagation)。
除了捕捉錯誤,我們也能「自己丟出錯誤」。
def getResult(s):
if 60<=score<=100:
return '及格'
elif(0<=score<60):
return '不及格'
else:
raise OverflowError
score=int(input('輸入成績:'))
try:
res = getResult(score)
except OverflowError:
print("成績數值錯誤")
else:
print("考試結果:", res)
輸出:
今天辛苦啦!
寫程式其實不是要避免錯誤,而是要知道如何面對錯誤~
例外處理就是那把讓程式變「成熟」的工具!
今天我們學會了:
try / except / else / finally 的完整架構、
as e 捕捉錯誤說明、raise 主動丟出例外
當你開始用「例外」來思考,而不是害怕紅字報錯,
你就不只是寫程式的人,而是能設計穩定系統的人!!
再堅持一下!我們要衝過終點線囉!!
那麼我們就明天見~