Python 錯誤和異常

錯誤和異常


在編寫代碼的時候,先無論出於什麼緣由,在運行階段,可能都看到過一些錯誤的信息。這些信息當中(至少)有兩種可區分的錯誤:語法錯誤異常python

語法錯誤


語法錯誤也叫解析錯誤,這可能在學習編碼中最容易遇到的錯誤:express

>>> while True print('Hello World')
  File "<stdin>", line 1
    while True print('Hello World')
                   ^
SyntaxError: invalid syntax

當出現錯誤時,解釋器會輸出出現語法錯誤的那行,同時會顯示 ^ 符號標記檢測到的錯誤。符號標記的位置表示錯誤出如今此處(或表示至少錯誤是在此處被檢測出)。在這個示例中,print() 函數被檢測出錯誤,這是由於前面的條件語句少了個冒號(:)。在錯誤信息中,會出現行號以及文件名,這都能方便在出現錯誤的時候到相應的位置檢查。微信

異常


有時候,檢查編寫的代碼發現並無什麼語法錯誤,但在執行時,仍是會引起錯誤。這種在執行時檢測到的錯誤被稱爲異常。大多數異常並不會被程序處理,會顯示以下的錯誤信息:網絡

>>> 10 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

>>> x + 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

>>> 1 + '1'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

如上示例,錯誤信息最後一行會告訴咱們程序遇到的是什麼類型的錯誤。在上面的例子中,出現的異常的類型分別是:ZeroDivisionErrorNameErrorTypeError。類型後面部分的信息則表示根據異常類型及其緣由提供詳細的信息。ide

異常處理


python 內置 try...except... 錯誤處理機制。函數

用例子看下 try...except... 機制:學習

try:
    r = 10 / 0
    print('r=', r)
except ZeroDivisionError as e:
    print('except:', e)

print('END')

程序運行結果以下:編碼

except: division by zero
END

在這裏說明一下 try 語句的工做原理:code

  • 首先,執行 try 子句(也就是 try 和 except 關鍵字之間的語句)。
  • 若是這個時候沒有異常產生,則會跳過 except 子句,完成 try 語句的執行。
  • 若是在執行 try 子句的時候發生異常,則會跳過該子句中剩下的部分(例如這個例子中,10 / 0 會產生異常,後面的 print('r=', r) 語句並無執行)。這個時候,若是異常的類型與 except 關鍵字後面的異常類型匹配,則會執行 except 子句。最後繼續執行整個 try 語句以後的代碼(例如本例子中 print('END'))。
  • 若是發生的異常和 except 關鍵字後面指定的異常不匹配,則會向外部進行傳遞;如果沒有找處處理程序,則認定爲未處理異常,執行的時候將會中止,並顯示前面說起的錯誤信息。

在這個例子當中, try 子句中的 10 / 0 出現異常,故後面的 print() 子句的內容並無輸出。而後,與 except 後面的異常匹配,因此輸出異常信息 except: division by zero,最後輸出整個 try 語句以後的 print('END') 語句。對象

這就是 try...except... 捕獲異常處理異常的大體原理。

try...except 語句後面還有一個可選的 else 子句,使用時放在 except 子句後面。在 try 子句不發生異常時執行。對前面的例子進行一些修改,好比:

try:
    r = 10 / 1
    print('r=', r)
except ZeroDivisionError as e:
    print('except:', e)
else:
    print('No error!')

print('END')

最終輸出的結果:

r= 10.0
No error!
END

拋出異常

raise 語句容許編碼者強制拋出指定異常。例如:

>>> raise NameError('An error occurred here')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: An error occurred here

raise 惟一的參數就是要拋出的異常。這個參數必須是一個異常實例或者是一個異常類(派生自 Exception 的類)。

若是是打算肯定是否發生了異常,但不打算處理的狀況下,可使用簡單的 raise 語句形式從新引起異常。好比:

>>> try:
...     raise NameError('An error occurred here.')
... except NameError:
...     print('An exception flew by!')
...     raise
...
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: An error occurred here.

自定義異常類

程序能夠經過建立新的異常類來命名本身的異常。自定義異常類一般直接或間接從 Exception 類派生。

class Error(Exception):
    '''基類
    '''
    pass

class InputError(Error):
    '''輸入表達式錯誤時引起異常

    Attributes:
        expression -- 輸入表達式
        message -- 異常輸出的信息
    '''
    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    '''狀態轉換不被容許時拋出異常

    Attributes:
        previous -- 轉換前的狀態
        next -- 轉換後新的狀態
        message -- 狀態轉換不被容許的說明
    '''
    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

通常狀況下,自定義異常名稱都以 Error 結尾,相似於標準異常的命名。

定義清理操做

try 語句還有一個可選子句 finally。用於定義必須在全部狀況下都執行的清理操做。例如:

>>> def divide(a, b):
...     try:
...         res = a / b
...     except ZeroDivisionError:
...         print('division by zero!')
...     else:
...         print('res =', res)
...     finally:
...         print('executing finally clause')
...
>>> divide(10, 1)
res = 10.0
executing finally clause

>>> divide(10, 0)
division by zero!
executing finally clause

>>> divide('10', '1')
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

在這裏,能夠看到,不管什麼狀況,finally 最終都會被執行。最後的例子中,因爲兩個字符串相除並無被 except 子句處理,這裏也在執行 finally 子句後,拋出 TypeError 異常。

finally 子句對於釋放外部資源(好比讀寫文件或者網絡鏈接)很是有用,不管是否成功使用資源。

預約義的清理操做

先看一段示例代碼:

for line in open('file.txt'):
    print(line, end='')

其實這段代碼有一個問題,它在這部分代碼執行結束以後,會在一段不肯定的時候,讓 file 對象一直保持打開的狀態。這裏可能對簡單的腳本並無太大的影響,但如果程序足夠大的話,這裏將會是個問題。

python 提供一個 with 語句,容許支持上下文管理協議的對象可以確保它們獲得及時和正確的清理:

with open('file.txt') as f:
    for line in f:
        print(line, end='')

在這裏,文件 f 始終會被關閉,不至於浪費資源。

參考資料


來源

  1. "8. Errors and Exceptions".docs.python.org.Retrieved 24 February 2020.
  2. "Exception hierarchy".docs.python.org.Retrieved 25 February 2020.

以上就是關於程序錯誤與異常的相關內容。

歡迎關注微信公衆號《書所集錄》
相關文章
相關標籤/搜索