Python程式設計

Lesson 12: 例外與異常處理

def div(a, b):
    """ 回傳a/b"""
    return a/b

num1 = div(100, 5)   # 100除以5
num2 = div(5, 8)     # 5除以8
num3 = div (6, 0)    # 6除以0???

程式難免出錯,但應該如何處理比較好?

在模組的第7行出錯

函式div()第3行所造成

錯誤類型是: ZeroDivisionError

原因是 division by zero(除數為0)

程式難免出錯,但應該如何處理比較好?

錯誤類型: ValueError

原因是 輸入內容無法轉成整數

while True:
    n = int(input("請輸入分數: "))
    if n==2:
      break
    if n==1:
      print('輸入指令1')
    # ...
    # 程式其他部分....
print('程式結束')
def div(a, b):
    """ 回傳a/b"""
    if b==0:        # 程式自行排除不適用的參數值
        print('除數不能為0')
        exit()   # 程式結束
    return a/b

# 主程式
print(div(100, 5))
print(div(5, 8))
print(div (6, 0))

解方1: 透過「邏輯判斷」自行排除不適用的狀況

當除數參數b為0時,便不做除法

結束程式

  1. 例外狀況排除的程式碼不容易寫
  2. 程式變得更複雜

通常不建議採用此方式處理

20.0
0.625
除數不能為0

執行結果

try:
    指令          # 可能發生例外狀況的指令
except 異常物件:     # 發生之異常狀況, 如ZeroDivisionError 
    異常狀況的處理程序    # 處理程序, 通常是印出異常訊息

解方2: 透過Python的「異常處理」語法處理

def div(a, b):
    """ 回傳a/b"""
    try:
        return a/b
    except ZeroDivisionError:
        print('除數不得為0')

# 主程式
print(div(100, 5))
print(div(5, 8))
print(div (6, 0))

20.0
0.625
除數不能為0

None

執行結果

try...except 語法

解方2: 透過Python的「異常處理」語法處理

try...except 語法

while True:
    try:
        n = int(input("請輸入分數: "))
        break
    except ValueError:
        print("輸入的不是整數, 請重新輸入")
print(f"讚!你輸入的整數是{n}")

函式版範例

try...except 語法

def int_input(prompt):
    while True:
        try:
            score = int(input(prompt)) # 若int()成功,執行下一行
            return score
        except ValueError as e:        # int()出現錯誤
            print("輸入的不是整數!請重新輸入")
            
myScore = int_input('輸入成績:')
if myScore >= 60:
    print(f'{myScore}分:及格')
else:
    print('f{myScore}分:不及格')

函式版範例

try...except 語法

def read_file(fn, encoding='utf-8'):
    try:
        with open(fn, encoding=encoding) as fobj:
            data = fobj.readlines()
            fobj.close()
        return data
    except FileNotFoundError:
        print(f'檔案{fn}找不到')
        exit(0)

# 主程式
fn = 'test.txt'
data = read_file(fn)
for line_num in range(len(data)):
    print(f'{line_num+1:>03}:{data[line_num].strip()}')

異常處理 try-except

  • try-except
  • 加上else區塊
  • 加上finally區塊
try:
  print(x)
except NameError:
  print("變數 x 未定義")

try-except語法: 處理單一錯誤

# 主程式開始
fn = 'test.py'
try:
    with open(fn, encoding='utf-8') as fobj:
        data = fobj.read()
except FileNotFoundError:
    print('檔案 %s 無法開啟' % fn)

變數未定義

檔案無法開啟

try:
  print(x)
except NameError:
  print("變數 x 未定義")
except:
  print("發生其他錯誤")
try:
    answer = x / y
except NameError:
    print('變數未定義')
except ZeroDivisionError:
    print('除法分母不得為0')
   

try-except語法: 處理多個錯誤

變數未定義 及 其他錯誤

變數未定義 及 除法錯誤

try-except語法: 印出系統提供的訊息

fn = 'test3.py'
try:
    with open(fn, encoding='utf-8') as fobj:
        data = fobj.read()
except FileNotFoundError:
    print('檔案 %s 無法開啟' % fn)
fn = 'test3.py'
try:
    with open(fn, encoding='utf-8') as fobj:
        data = fobj.read()
except FileNotFoundError as error_msg:
    print(error_msg)

檔案 test.py 無法開啟

執行結果

[Errno 2] No such file or directory: 'test3.py'

執行結果

try:
    指令          # 可能發生例外狀況的指令
except 異常物件:     # 發生之異常狀況, 如ZeroDivisionError 
    異常狀況的處理程序    # 處理程序, 通常是印出異常訊息
else:
    正常的處理程序

try...except else:  指令正常時,執行else區塊

try-except語法的變形: try-except-else

def div(a, b):
    """ 回傳a/b"""
    try:
        return a/b
    except ZeroDivisionError:
        print('除數不得為0')

# 主程式
print(div(100, 5))
print(div(5, 8))
print(div (6, 0))

20.0
0.625
除數不能為0

None

執行結果

try-except-else範例: 檔案無法開啟

# 主程式開始
fn = 'test.py'
try:
    with open(fn, encoding='utf-8') as fobj:
        data = fobj.read()
except FileNotFoundError:
    print('檔案 %s 無法開啟' % fn)
else:
    print(data)

檔案 test.py 無法開啟


 

執行結果

...
area = 3.14 * r**2     # 指定運算子
print('面積是 %8.2f' % area)
...

執行結果

函式內未處理,也可由呼叫端處理「異常狀況」

# 函式openFile
def openFile(fn, code='cp950'): 
    # 未處理異常狀況
    with open(fn, encoding=code) as fobj:
        data = fobj.read()
        return data
# 主程式開始
fn = 'test.py'
try:
    data = openFile(fn,'utf-8') #也可由呼叫函式端處理
except FileNotFoundError:
    print('檔案 %s 無法開啟' % fn)
else:
    print(data)

檔案 test.py 無法開啟


 

執行結果

...
area = 3.14 * r**2     # 指定運算子
print('面積是 %8.2f' % area)
...

執行結果

常見「異常類型」與通用型Exception

異常物件類型
ZeroDivisionError 除數為0的錯誤
FileNotFoundError 找不到所指定的檔案
Exception 通用型,所有錯誤皆可使用
IndexError 索引值超出範圍
IOError 輸出或輸入發生的錯誤(例如: 檔案找不到)
SyntaxError 語法錯誤
TypeError 資料型別錯誤

Exception包含所有異常物件

IOError包含FileNotFoundError

# 主程式開始
fn = 'test.txt'
try:
    with open(fn) as fobj:
        data = fobj.read()
except FileNotFoundError:
    print('檔案 %s 無法開啟' % fn)
else:
    print(data)
# 主程式開始
fn = 'test.txt'
try:
    with open(fn) as fobj:
        data = fobj.read()
except Exception:
    print('發生錯誤')
else:
    print(data)
# 主程式開始
fn = 'test.txt'
try:
    with open(fn) as fobj:
        data = fobj.read()
except IOError:
    print('發生IO錯誤')
else:
    print(data)

不保證是FileNotFoundError!

不保證是IOError!

finally區塊: 必須執行的區塊

try:
    指令          # 可能發生例外狀況的指令
except 異常物件:     # 發生之異常狀況, 如ZeroDivisionError 
    異常狀況的處理程序    # 處理程序, 通常是印出異常訊息
else:
    正常的處理程序
finally:
    必須執行的區塊  # 異常 or 正常程序處理後,都要執行此處
try:
    fobj = open('test.txt')  # 假設檔案存在
    data = fobj.read()
except IOError as msg:
    print(msg)
else:
    print(data)
finally:
    fobj.close()  # 不管有無發生讀寫異常, 檔案都須關閉