except -- 例外處理
在程式設計上,常見的錯誤可分為兩類,一類是「
語法錯誤」,一類是「
例外錯誤」。
語法錯誤
# if 最後面少了冒號
if x>100
print('x=', x)
語法錯誤較容易發現,因為只要程式一執行,
Python 直譯器就會指出錯誤的地方了
File "practice.py", line 2
if x>100
^
SyntaxError: invalid syntax
例外錯誤就比較麻煩了,大多數的情況程式都可以正常執行,但就會因某個特殊情況導致程式當掉。
程式一當掉,發生錯誤的地方之後的程式碼也就不會執行了。例如:
# 只要使用者不輸入 0
# 程式會無止盡的執行下去
while True:
x = int(input('請輸入任意數字'))
print('100 /', x, '=',100/x)
# 但是,一旦使用者輸入 0
# 就爆出錯誤訊息了
Traceback (most recent call last):
File "practice.py", line 4, in <module>
print('100 /', x, '=',100/x)
ZeroDivisionError: division by zero
所以,要避免預期外的未爆燀,就要以「
例外處理」預防
例外處理的語法:
try:
指令 1
指令 2
..
..
except 例外類別一:
# 當預期外的錯誤發生時
# 要做的補救措施
指令 1
指令 2
...
...
except 例外類別二:
# 當預期外的錯誤發生時
# 要做的補救措施
指令 1
指令 2
...
...
else:
# 當沒有例外錯誤發生時會執行本段指令
指令 1
指令 2
...
...
finally:
# 無論有沒有發生錯誤, 都會執行
指令 1
指令 2
...
...
前面的範例加上例外處理機制:
try:
while True:
x = int(input('請輸入任意數字'))
print('100 /', x, '=',100/x)
except ZeroDivisionError: # 遇到分母為 0 的意外類別時要做的處置
# 一旦程式進到這裡
# 表示已離開迴圈, 不會再無止盡的執行
print('分母不可以是 0')
這樣子,遇到使用者輸入 0 時,就會提示不可輸入 0,而不會爆出錯誤了,不過程式也因此停止了。
將程式的順序調整一下
while True:
try:
x = int(input('請輸入任意數字'))
print('100 /', x, '=',100/x)
except ZeroDivisionError:
print('分母不可以是 0')
就可以像原本一樣,即使使用者輸入 0,會提示使用者不可輸入 0,並繼續讓使用者輸入。
一般的情境會是這樣,只要沒有遇到例外,就一路往下執行;當例外發生時,就以
except: 內的程式處理、排除,處理完後,回到原本要正常執行的地方重來一遍,一旦結果執行正常,就繼續往下走。
所以正常情況下,
while 迴圈只會執行一次
while True:
try:
x = int(input('請輸入任意數字: '))
print('100 /', x, '=',100/x)
break
except ZeroDivisionError:
print('分母不可以是 0')
print('程式沒爆錯誤訊息, 繼續往下執行')
except 則是在
try 語法中,當程式正常執行,沒有發生例外錯誤時,要繼續執行的程式區段;所以
else 必須放在
except 之後。
上面的範例,我們改將
break 放到
else 區段
while True:
try:
x = int(input('請輸入任意數字: '))
print('100 /', x, '=',100/x)
except ZeroDivisionError:
print('分母不可以是 0')
else:
break
print('程式沒爆錯誤訊息, 繼續往下執行')
執行結果
請輸入任意數字: 9
100 / 9 = 11.11111111111111
程式沒爆錯誤訊息, 繼續往下執行
除了
ZeroDivisionError 外,還有
NameError、
TypeError、...等,這些稱為
Python 「
內建的例外錯誤」,見下表
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
| +-- ModuleNotFoundError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
有些例外類別還可細分出子類別,例如
ZeroDivisionError 與
FloatingPointError、
OverflowError 皆屬於
ArithmeticError 的子類別
除了輸入 0,還要防止使用者輸入不是數字的情況
while True:
try:
x = int(input('請輸入任意數字'))
print('100 /', x, '=',100/x)
except ZeroDivisionError:
print('分母不可以是 0')
except ValueError:
print('您輸入的不是數字')
若
except 後面不加參數,則所有會引起例外的錯誤都會被觸發,所以這種用法要小心
# 注意這裡引入了系統函式模組
# 並移除了 while 迴圈
import sys
try:
x = int(input('請輸入任意數字'))
print('100 /', x, '=',100/x)
except ZeroDivisionError:
print('分母不可以是 0')
except ValueError:
print('您輸入的不是數字')
except:
# 顯示觸發錯誤的類別
print('例外錯誤:', sys.exc_info()[0])
執行結果
# 當按下 Ctrl + C 時, 也會觸發例外
請輸入任意數字例外錯誤: <class 'KeyboardInterrupt'>
從上表知道,
Exception 底下有最多的子例外類別,所以您可以在最後以
Exception 捕捉其餘會發生的例外錯誤
try:
x = int(input('請輸入任意數字'))
print('100 /', x, '=',100/x)
except ZeroDivisionError:
print('分母不可以是 0')
except ValueError:
print('您輸入的不是數字')
except Exception as e:
print('例外錯誤,', '類別:', type(e), '說明:', str(e))
最後是
finally,必須在整個
try 語法的最末,無論有沒有例外錯誤發生,
finally 區段內的指令都會執行
while True:
try:
x = int(input('請輸入任意數字: '))
print('100 /', x, '=',100/x)
except ZeroDivisionError:
print('分母不可以是 0')
except ValueError:
print('您輸入的不是數字')
except Exception as e:
print('例外錯誤,', '類別:', type(e), '說明:', str(e))
else:
break
finally:
print('不管有沒有例外錯誤發生,我都會出現')
print('程式沒爆錯誤訊息, 繼續往下執行')
執行結果:
# 正常執行, 沒有例外錯誤發生
請輸入任意數字: 15
100 / 15 = 6.666666666666667
不管有沒有例外錯誤發生,我都會出現
程式沒爆錯誤訊息, 繼續往下執行
# 輸入 n, 觸發例外錯誤
請輸入任意數字: n
您輸入的不是數字
不管有沒有例外錯誤發生,我都會出現
請輸入任意數字:
在開發程式的過程中,我們可以主動以
raise 指令觸發例外錯誤,以驗證
except 區段的程式執行結果是否真照我們預期。
try:
raise ValueError
except ValueError:
print('您輸入的不是數字')
雖然
raise 後可以不接任何東東,不過習慣上,我們會接上要觸發的例外類別。