python異常處理

在寫python代碼的過程中可能會出現一些錯誤,這些錯誤可區分爲2類:語法錯誤 和 異常~

語法錯誤

>>> a = 2
>>> if a > 0 print('OK');
  File "<stdin>", line 1
    if a > 0 print('OK');
                 ^
SyntaxError: invalid syntax

 
如上語句即 存在語法錯誤,if 語句的條件後面沒有冒號 ‘:’。
語法錯誤也稱爲解析錯誤,這類錯誤在程序執行之前就會被Python解釋器檢測到。如果使用IDE工具開發(例如 pycharm),pycharm會直接使用下標的紅線提示開發者這裏存在語法錯誤。這類錯誤必須在程序運行前就改正~
python異常處理

異常

代碼不存在語法錯誤,在運行過程中出現的邏輯錯誤,即爲異常~

>>> 1 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 
>>> 
>>> lst = ['a', 'b', 'c']
>>> lst[2]
'c'
>>> lst[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

 
程序在運行過程中,一旦發生異常,且發生的異常沒有被捕獲或者處理,則程序就會停止運行。
 
異常有不同的類型,在發生異常時,異常的類型及發生異常的原因 會被打印出來。上述示例中,第一個異常爲 ZeroDivisionError(除數不能爲0),第二個異常爲 IndexError(列表指針超出範圍)。
 
ZeroDivisionError 和 IndexError 都爲Python的內置異常,即 python解釋器 已經定義好的異常類型,常見的內置異常還有:StopIteration,GeneratorExit,AssertionError,EOFError,IOError,OSError等等。關於Python的內置異常及說明可參閱:https://docs.python.org/zh-cn/3.7/library/exceptions.html#bltin-exceptions
 

這些內置異常都繼承於Exception類(自定義的異常一般也是直接或間接地繼承自Exception類),且所有 內置異常的名稱都存在於 內置名稱空間中,可直接使用,而不需要 'import exceptions模塊'。

異常處理

try...except... 語句

try...except... 語句用於捕獲異常 及 對異常的處理。這樣程序在發生異常時,可針對性的做出處理,避免程序的中斷~,try...except... 語句的語法如下:

try:
    可能發生異常的語句塊
except [異常類型1]:
    處理異常1的語句
except [異常類型2]:
    處理異常2的語句

示例:

try:
    a = 1/0
except ZeroDivisionError:
    print('除數不能爲0')

 
try...except... 語句的工作原理如下:

  • 首先自上而下 執行 try 語句塊中的代碼(一般將可能發生異常的語句寫在 try語句塊 中)
  • 如果沒有發生異常,try語句塊 中的代碼執行完成後,將跳過 except語句塊,繼續向下執行
  • 如果 try語句塊 中有異常發生,則 try語句塊 中剩下的代碼將不會被執行。如果這個異常類型和 某一個 except 關鍵字後面的異常匹配,則執行這個 except 中的子句。
  • 如果 try語句塊 中出現的異常類型 和所有 except 關鍵字後面的異常都不匹配,則這個異常會被傳遞到外部的 try 語句中,如果外部沒有做異常處理,程序將中斷並打印錯誤信息~
     
    若 try語句塊 中除了 ZeroDivisionError 異常外,還存在別的異常:
    try:
    num = int(input("Enter a number: "))    # 這裏可能會發生 ValueError 異常
    a = 1/0
    except ZeroDivisionError:
    print('除數不能爲0')

    對於可能會先發生的 ValueError 異常,異常類型和 except 關鍵字後面的異常不匹配,那麼這個 異常 將不會被 這個except 捕獲,於是這個異常就會被傳遞到外部~,結果輸出如下:

    Enter a number: abc
    Traceback (most recent call last):
    File "...test.py", line 9, in <module>
    num = int(input("Enter a number: "))    # 這裏可能會發生 ValueError
    ValueError: invalid literal for int() with base 10: 'abc'

     
    這個時候,可以使用多個 except 來捕獲異常:

    try:
    num = int(input("Enter a number: "))    # 這裏可能會發生 ValueError
    a = 1/0
    except ZeroDivisionError:
    print('除數不能爲0')
    except ValueError:
    print('請輸入數字~')
    # 結果輸出:
    Enter a number: abc
    請輸入數字~

    當然也可以使用一個萬能異常 Exception 來處理。由於內置異常和自定義異常都直接或間接地繼承自 Exception,所以 Exception 可以用來匹配任何異常。這樣做的優勢在於 可以針對所有的異常統一進行捕獲,缺點在於 不能針對指定的異常做出相應的處理,而是統一使用一種方式進行處理~

    try:
    num = int(input("Enter a number: ")) 
    a = 1/0
    except Exception:
    pass

     
    在實際的應用中,一般這兩種方式會被結合使用:針對指定的那麼幾個異常分別進行捕獲並做相應的處理,剩下的所有異常都統一使用 Exception 來捕獲~

    try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
    except OSError as err:
    print(err)
    except ValueError:
    print("數據類型無法轉換")
    except Exception:
    print('Unexpected error')

    最後的 Exception異常名 也可以省略不寫(默認就是 Exception )

    except:
    print('Unexpected error')

as 獲取異常信息

異常發生時,都會有關於這個異常的具體描述信息,異常的描述信息可通過 as 關鍵字獲取,這個在上述示例中已經有使用~

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print(err)
# 若文件不存在,或有如下輸出:
[Errno 2] No such file or directory: 'myfile.txt'

try...except...else... 語句

else 爲可選字句,放在 except 字句後面,若 try 語句塊中沒有異常發生,則會執行else 語句塊中的代碼~

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print(err)
else:
    f.close()        # try 語句塊正常執行完成後,關閉打開的文件

try...except...finally... 語句

finally 也爲可選字句,放在所有語句塊的最後。不管 try 語句塊中是否觸發異常,都會執行 finally 語句塊中的代碼。finally 語句塊中的代碼一般都用於 關閉系統資源,例如關閉文件,關閉數據庫連接等~

def connDB():
    try:
        conn = pymysql.connect(host=HOST, user=USER, password=PASSWD, database=DATEBASE, charset='utf8', cursorclass=pymysql.cursors.DictCursor)
        return conn
    except Exception:
        logger.error("can not connect to database!")
        exit()

db = connDB()

try:
        ...
    pass
except:
        ...
    pass
finally:
    db.close()

 
在操作文件時,還可以使用 with...as... 語句來自動關閉文件,即不管 with語句塊 中是否觸發異常,with語句塊執行完畢後都會自動關閉文件~

try:
    f = open('myfile.txt', mode='r')
    a = 1/0
except Exception:
    print(f.closed)    # False,當 try語句塊中發生異常,文件沒有被關閉 
##############
try:
    with open('myfile.txt', mode='r') as f:
        a = 1 / 0
except Exception:
    print(f.closed)    # True,文件已經自動關閉

raise觸發異常

raise語句用於手動觸發異常,觸發的異常可以通過傳遞參數說明異常的原因,例如:

>>> raise IndexError('lst out of range')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: lst out of range

 
在 except語句塊中,若不想對捕獲的異常進行處理,則可以使用 raise語句 重新觸發異常,由外層的 try語句來進行處理~

try:
    raise ZeroDivisionError('abc')
except ZeroDivisionError:
    print('除數爲0')
    raise    # 更簡單的寫法,重新觸發捕獲到的異常

assert 語句

assert語法:

assert expression

assert 關鍵字根據後面的表達式的真假來確定程序是否繼續往下執行。若表達式返回爲True,則程序繼續往下執行,若爲False,則觸發 AssertionError 異常~

print('start...')
assert 1 == 1
print('end...')
# 輸出結果:
start...
end...
################
print('start...')
assert 1 == 2
print('end...')
# 輸出結果:
start...
Traceback (most recent call last):
  File ".../test.py", line 17, in <module>
    assert 1 == 2
AssertionError

自定義異常

通過繼承 Exception 類來創建自定義的異常;自定義異常應該儘量保持異常類的簡單,一般自定義異常僅用於提取特有的錯誤信息~。自定義異常示例如下:

class MyError(Exception):
    def __init__(self, ErrorInfo):
        Exception.__init__(self)
        self.errorinfo=ErrorInfo
    def __str__(self):
        return self.errorinfo

 
注意:自定義的異常類只能通過 raise關鍵字 來手動觸發。

traceback追蹤異常

使用 traceback 關鍵字追蹤異常,需要導入 traceback 模塊(import traceback)。

import traceback
try:
    f = open('myfile.txt', mode='r')
    a = 1/0
except Exception:
    traceback.print_exc()       
    logger.error(traceback.format_exc())    #使用日誌模塊記錄 異常的詳細信息,或使用 traceback.print_exc(file=path) 記錄到指定文件
# 輸出信息如下:
Traceback (most recent call last):
  File "/Users/luyi/PycharmProjects/untitled/day26/test.py", line 12, in <module>
    a = 1/0
ZeroDivisionError: division by zero

這裏異常信息的輸出和不捕獲異常時候系統的輸出一致,其實 traceback.print_exc() 函數調用了sys.exc_info() 函數來完成 異常信息的輸出,traceback.print_exc()的源碼如下:

def print_exc(limit=None, file=None):
    """Shorthand for 'print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)'.
    (In fact, it uses sys.exc_info() to retrieve the same information
    in a thread-safe way.)"""
    if file is None:
        file = sys.stderr
    try:
        etype, value, tb = sys.exc_info()      # 調用了 sys.exc_info() 函數
        print_exception(etype, value, tb, limit, file)
    finally:
        etype = value = tb = None

.................^_^

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章