Coding中 / EN / 日
PEP 765 try finally迷思
解析 PEP 765 為何禁止在 finally 區塊中使用 return、break、continue,並延伸介紹 with 資源管理與 Python 3.11 的 ExceptionGroup 與 except* 例外處理機制。
tl;dr
根據 PEP 765,在 try-except-finally 的流程中,return, break, continue不應該在finally 區塊中使用,否則會導致原本應該被捕捉或傳遞的錯誤消失,彷彿錯誤從未發生過。
因此,自 Python 3.14 起,若在 finally 中使用這些語句,將會觸發 Syntax Warning。
為什麼要使用 try-except-finally?
以前寫程式時常會使用 try-except-finally,但沒有深入思考最佳實踐。透過這次 PEP 的發布,更了解官方建議的使用方式。
try-except 用於預期可能發生錯誤的區塊,並透過 except 對不同類型的錯誤做個別處理。以下是一個模擬訂單系統的範例:
try:
Order.insert('Shoes', 1)
except IntegrityError:
print("由於完整性錯誤,訂單新增失敗")
except Exception as e:
print(f"發生未預期的錯誤:{e}")
else:
print('訂單成功建立')
finally:
print('訂單處理流程結束')這段程式碼中:
try嘗試建立訂單。except捕捉特定錯誤並分別處理。else在沒有任何錯誤時執行。finally無論是否發生錯誤都會執行,例如用來釋放資源或紀錄日誌。
使用 with 來更安全地釋放資源
雖然 finally 常用於清理資源,但還是使用 with 語句,它能自動管理資源的開啟與釋放,會遠比 finally 來得簡潔跟有邏輯。
with open('order.txt', 'w') as f:
f.write('New order')這樣即使過程中發生例外,檔案也會自動關閉,不需要額外寫 finally。
進階應用:Exception Groups
在 Python 3.11 以前,例外處理一次只能傳播一個錯誤。雖然可以用例外鏈 (PEP 3134) 表示錯誤因果關係,但當多個錯誤同時發生、卻彼此無關時,就很難同時處理。為了解決這個問題,ExceptionGroup 與 except* 語法應運而生。
try:
raise ExceptionGroup("多個錯誤", [ValueError("錯誤1"), TypeError("錯誤2")])
except* ValueError as e:
print(f"捕捉到 ValueError: {e}")
except* TypeError as e:
print(f"捕捉到 TypeError: {e}")主要用途與動機包括:
- 同時報告多個錯誤:封裝並引發多個無關錯誤,避免只看到第一個錯誤。
- 並發任務錯誤處理:例如在
asyncio.gather()同時有多個任務失敗時,一次報告所有例外。 - 統一處理裸例外 (naked exception):在
except*中捕捉一般例外時會自動包成ExceptionGroup,確保型別一致性。
總而言之ExceptionGroup 讓 Python 能好的處理多種拋錯。
last edited 2025.05.07·© 2025 tylerastro·built by hand