Python 3 學習筆記之——錯誤和異常

seniusen發表於2018-10-28

1. 語法錯誤

Python 的語法錯誤被稱為解析錯,語法分析器會指出出錯的程式碼行,並且在最先找到的錯誤的位置標記一個小小的箭頭。

>>> while True 
  File "<stdin>", line 1
    while True 
              ^
SyntaxError: invalid syntax

2. 異常

即使 Python 程式的語法是正確的,但是在執行的時候,也有可能發生錯誤,執行期監測到的錯誤稱為異常。

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

>>> a = [1, 2, 3]
>>> b = a(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not callable

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

3. 異常處理

try 語句按照以下方式工作:

  • 首先,執行 try 子句,也就是在 try 和 except 之間的語句
  • 如果沒有異常發生,忽略 except 子句,程式繼續執行
  • 如果在 try 子句執行過程中發生了異常,那麼 try 子句餘下的部分將被忽略
  • 若異常的型別和 except 之後的名稱相符,那麼對應的 except 子句將被執行,然後再執行 try 語句之後的程式碼
  • 若異常沒有與任何的 except 匹配,那麼這個異常將會傳遞到上層的 try 中去
while True:
    try:
        x = int(input("Please enter a number: "))
        break
    except ValueError:
        print("Oops!  That was no valid number.  Try again!")

Please enter a number: we
Oops!  That was no valid number.  Try again!
Please enter a number: sd
Oops!  That was no valid number.  Try again!
Please enter a number: 23

在上面的例子中,我們讓使用者通過鍵盤來輸入一個合法的整數。如果輸入的是字串,那 int 函式就無法將字串轉為整數,程式就會丟擲一個異常,然後執行 except 子句,列印資訊提示我們重新輸入,直到輸入一個整數程式 break 結束。

一個 except 子句可以同時處理多個異常,這些異常被放在一個括號裡成為一個元組。

except (RuntimeError, TypeError, NameError):
    pass

最後一個 except 子句可以忽略異常的名稱,它將被當做萬用字元使用。此時可以列印出錯誤資訊,然後再次把異常丟擲。

import sys

try:
    f = open('test.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

# 沒有相應檔案時報錯
OS error: [Errno 2] No such file or directory: 'test.txt'

# 檔案中第一行為字串無法轉化為整數時報錯
Could not convert data to an integer.

try except 語句還有一個可選的 else 子句,如果使用這個子句,那麼必須放在所有的 except 子句之後。這個子句將在try子句沒有發生任何異常的時候執行。

import sys

try:
    f = open('test.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

else:
    print(i)

# 1

使用 else 子句比把所有語句都放在 try 子句裡面要好,這樣可以避免一些意想不到的而 except 又沒有捕獲到的異常

異常處理也可以處理子句中呼叫函式甚至是間接呼叫函式裡丟擲的異常。

3. 使用者自定義異常

我們可以通過建立一個新的異常類來擁有自己的異常。異常類繼承自 Exception,可以直接繼承,或者間接繼承。

class MyError(Exception):
    
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

try:
    raise MyError(2*2)
except MyError as e:
    print('My exception occurred, value:', e.value)

# My exception occurred, value: 4

4. 定義清理行為

try 語句還有另外一個可選的子句 finally,它定義了無論任何情況下都會執行的清理行為

def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("division by zero!")
    else:
        print("result is", result)
    finally:
        print("executing finally clause")
   
divide(2, 1)
result is 2.0
executing finally clause

divide(2, 0)
division by zero!
executing finally clause
divide('2', '1')
executing finally clause

TypeError     Traceback (most recent call last)
<ipython-input-19-0fe65f250ff1> in <module>()
----> 1 divide('2', '1')

<ipython-input-16-fb375c737bea> in divide(x, y)
      1 def divide(x, y):
      2     try:
----> 3         result = x / y
      4     except ZeroDivisionError:
      5         print("division by zero!")

TypeError: unsupported operand type(s) for /: 'str' and 'str'

一些物件定義了標準的清理行為,當程式中不需要它的時候,那麼這個標準行為就會執行。關鍵詞 with 語句可以保證諸如檔案之類的物件在使用完之後一定會正確=地執行清理行為

參考資料 菜鳥教程

獲取更多精彩,請關注「seniusen」!

相關文章