python 例外處理

2023-01-02 06:00:31

python 例外處理

異常機制本質

異常是指程式執行過程中出現的非正常現象,例如使用者輸入錯誤、除數為零、需要處理的檔案不存在等

所謂例外處理,就是指程式再出現問題時依然可以正確的執行剩餘的程式,而不會因為異常而終止程式的執行

python 中,引進了很多用來描述和處理異常的類,稱為異常類。異常類定義中包含了該類異常的資訊和對異常進行處理的方法。

python 中一切都是物件,異常也採用物件的方式來處理。處理過程:

  1. 丟擲異常: 在執行一個方法時,如果發生異常,則這個方法生成代表該異常的一個物件,停止當前執行路徑,並把異常物件提交給直譯器。
  2. 捕獲異常: 好釋器得到該異後,尋相應的程式碼本處理該異。

try...except...

try:
    a = 3 / 0
except ZeroDivisionError as error:
    print(error) 

# result
# division by zero

try...except...except...

try:
    while True:
        a = int(input("請輸入一個數位:"))
        if a == 888:
            break
        b = int(input("請輸入一個數位:"))
        if b == 888:
            break
        print("正在計算{0} 除以 {1}".format(a, b))
        calculate = a / b
        print("結果為:{0}".format(calculate))
except ZeroDivisionError:
    print("0 不能做除數!!")
except ValueError:
    print("您輸入的不是數位!!!")

"""
result:
請輸入一個數位:1
請輸入一個數位:0
正在計算1 除以 0
0 不能做除數!!
===========================
請輸入一個數位:1
請輸入一個數位:abc
您輸入的不是數位!!!

"""

try...except...else

try...except...else結構增加了「else塊」,如果try中沒有丟擲異常,則執行else塊,如果try塊中丟擲異常,則執行excetp,不執行else塊。

# coding = utf-8
try:

    a = int(input("請輸入一個數位:"))

    b = int(input("請輸入一個數位:"))

    print("正在計算{0} 除以 {1}".format(a, b))

    calculate = a / b

except ZeroDivisionError:
    print("0 不能做除數!!")
except ValueError:
    print("您輸入的不是數位!!!")
else:
    print("結果為:{0}".format(calculate))

"""
result:
請輸入一個數位:1
請輸入一個數位:10
正在計算1 除以 10
結果為:0.1
================================
請輸入一個數位:2
請輸入一個數位:0
正在計算2 除以 0
0 不能做除數!!
"""

try...except...else...finally

try...except...else...finally結構中,finally塊無論是否發生異常都會被執行,通常用來釋放try塊中申請的資源。

try:

    a = int(input("請輸入一個數位:"))

    b = int(input("請輸入一個數位:"))

    print("正在計算{0} 除以 {1}".format(a, b))

    calculate = a / b

except ZeroDivisionError:
    print("0 不能做除數!!")
except ValueError:
    print("您輸入的不是數位!!!")
else:
    print("結果為:{0}".format(calculate))
finally:
    print("程式結束")

"""
result:
請輸入一個數位:1
請輸入一個數位:0
正在計算1 除以 0
0 不能做除數!!
程式結束
==============================
請輸入一個數位:1
請輸入一個數位:2
正在計算1 除以 2
結果為:0.5
程式結束
"""

常見異常彙總和說明

return 語句和例外處理問題

由於 return 有兩種作用:結束方法執行、返回值。我們一般不把 return 放到例外處理結構中,而是放到方法最後。
異常結構中return的使用方式

def test01():
    print('step1')
    try:
        x = 3 / 0
        # return 'a'
    except:
        print('step2')
        print('異常:0不能作為除數')
        # return 'b'
    finally:
        print('step3')
        # return 'c'

    print('step4')
    return 'd'  # 老師:一般不要把return語句放到try、except、else、finally塊中,會發生一些意想不到的錯誤。建議放到最後。


print(test01())

"""
result:

step1
step2
異常:0不能作為除數
step3
step4
d
"""

return不在最後,所以執行到return本來程式執行要終止,但是還是會把finally塊的程式碼執行完,之後就不執行了。
異常結構中return的使用方式

def test01():
    print('step1')
    try:
        x = 3 / 0
        # return 'a'
    except:
        print('step2')
        print('異常:0不能作為除數')
        return 'b'
    finally:
        print('step3')
        # return 'c'

    print('step4')
    # return 'd'  # 一般不要把return語句放到try、except、else、finally塊中,會發生一些意想不到的錯誤。建議放到最後。


print(test01())

"""
result:
step1
step2
異常:0不能作為除數
step3
b
"""

常見異常彙總

異常名稱 說明
ArithmeticError 所有數值計算錯誤的基礎類別
AssertionError 斷言語句失敗
AttributeError 物件沒有這個屬性
BaseException 所有異常的基礎類別
DeprecationWarning 關於被棄用的特徵的警告
EnvironmentError 作業系統錯誤的基礎類別
EOFError 沒有內建輸入,達到EOF標記
Exception 常規錯誤的基礎類別
FloatPointError 浮點計算錯誤
FutureWarning 關於構造語意將來會有改變的警告
GeneratorExit 生成器(generator)發生異常,通知退出
ImportError 匯入模組/物件失敗
IndentationError 縮排錯誤
IndexError 序列中沒有此索引
IOError 輸入/輸出操作失敗
KeyboardInterrupt 使用者中斷執行(通常是輸入^C)
KeyError 對映中沒有這個鍵
LookupError 無效查詢的基礎類別
MemoryError 記憶體溢位錯誤(對Python直譯器來說不是致命的)
NameError 未宣告/初始化物件(沒有屬性)
NotImplementedError 尚未實現的方法
OSError 作業系統錯誤
OverflowError 數值運算超出最大限制
OverflowWarning 舊的關於自動提升為長整型(long)的警告
PendingDeprecationWarning 關於特性將會被廢棄的警告
ReferenceError 弱參照(Weak Reference)試圖存取已經垃圾回收了的物件
RuntimeError 一般的執行時錯誤
RuntimeWarning 可以的執行時行為(Runtime Behavior)的警告
StandardError 所有內建標準異常的基礎類別
StopIteration 迭代器沒有更多的值
SyntaxError Python語法錯誤
SyntaxWarning 可以的語法的警告
SystemError 一般的直譯器系統錯誤
SystemExit 直譯器請求退出
TabError Tab和空格鍵混用
TypeError 對型別無效的操作
UnboundLocalError 存取未初始化的本地變數
UnicodeDecodeError Unicode編碼時的錯誤
UnicodeError Unicode相關的錯誤
UnicodeTranslateError Unicode轉換時的錯誤
UserWarning 使用者程式碼生成的警告
ValueError 傳入無效的引數
Warning 警告的基礎類別
WindowsError 系統呼叫失敗
ZeroDivisionError 除(或取模)零(所有資料型別)

with…as語句

with通過__enter__方法初始化,然後在__exit__中做善後以及處理異常。其中__enter__()方法在語句體(with語句包裹起來的程式碼塊)執行之前進入執行,exit()方法在語句體執行完畢退出後執行。

with 語句適用於對資源進行存取的場合,確保不管使用過程中是否發生異常都會執行必要的「清理」操作,釋放資源,比如檔案使用後自動關閉、執行緒中鎖的自動獲取和釋放等。

With語句的基本語法格式:

with expression [as target]:
	with-block 
    
expression:是一個需要執行的表示式;
target:是一個變數或者元組,儲存的是expression表示式執行返回的結果,可選引數。

with語句原理

上下文管理協定(Context Management Protocol):包含方法 enter()和__exit__(),支援該協定的物件要實現這兩個方法。

上下文管理器(Context Manager):支援上下文管理協定的物件,這種物件實現了__enter__()和__exit__()方法。上下文管理器定義執行with語句時要建立的執行時上下文,負責執行with語句塊上下文中的進入與退出操作。通常使用with語句呼叫上下文管理器,也可以通過直接呼叫其方法來使用。
例:執行過程:

with EXPR as VAR:
    BLOCK
  1. 執行EXPR,生成上下文管理器context_manager;

  2. 獲取上下文管理器的__exit()__方法,並儲存起來用於之後的呼叫;

  3. 呼叫上下文管理器的__enter__()方法;如果使用了as子句,則將__enter__()方法的返回值賦值給as子句中的VAR;

  4. 執行BLOCK中的表示式;

  5. 不管是否執行過程中是否發生了異常,執行上下文管理器的__exit__()方法,exit()方法負責執行「清理」工作,如釋放資源等。如果執行過程中沒有出現異常,或者語句體中執行了語句break/continue/return,則以None作為引數呼叫__exit__(None, None, None);如果執行過程中出現異常,則使用sys.exc_info得到的異常資訊為引數呼叫__exit__(exc_type, exc_value, exc_traceback);

  6. 出現異常時,如果__exit__(type, value, traceback)返回False,則會重新丟擲異常,讓with之外的語句邏輯來處理異常,這也是通用做法;如果返回True,則忽略異常,不再對異常進行處理。

trackback 模組

程式很多時候是伺服器在執行,不會時時刻刻有人在電腦前看著,應用Trackback模組將錯誤資訊寫入紀錄檔檔案中,可以便於運維人員定期去翻閱異常記錄檔案查錯。

*【例】使用 Traceback 模組列印異常資訊 *

import traceback

try:
    print('step1')
    num = 1 / 0
except:
    # open()的'a'模式(可以聯絡append方法去理解和記憶),開啟一個檔案用於追加。如果該檔案已
    with open(r"traceback.txt", 'a') as f:
        # 存在,檔案指標將會放在檔案的結尾,也就是說新的內容會被寫入到已有內容之後;如果該檔案不存在,建立新檔案進行寫入。
        traceback.print_exc(file=f)

raise自定義異常類

程式開發中,有時候我們也需要自己定義異常類。自定義異常類一般都是執行時異常,通常繼承 Exception或其子類即可。命名一般以Error、Exception 為字尾。

自定義異常由 raise 語句主動丟擲,一般自定義異常需要重寫__str__方法:

class AgeError(Exception):

    def __init__(self, errorinfo):
        super().__init__(self)
        self.errorinfo = errorinfo

    def __str__(self):
        return str(self.errorinfo) + "這個年齡超出範圍,"+ "年齡應該在1到150之間"


if __name__ == "__main__":
    age = int(input("輸入一個年齡:"))
    if age > 1 and age < 150:
        print("年齡是:{0}".format(age))
    else:
        raise AgeError(age)

"""
result:

輸入一個年齡:19
年齡是:19
=============================

Traceback (most recent call last):
  File "c:\Users\chenh\OneDrive\Data Learn\Python 基礎\課堂筆記\09\try.py", line 16, in <module>
    raise AgeError(age)
AgeError: 200這個年齡超出範圍,年齡應該在1到150之間

"""