python基礎教程:錯誤處理和異常處理
在前面介紹Python語法的過程中,我們已經接觸到了直譯器給的錯誤和異常,但並沒有詳細講解它們。現在我們就全面的來學習Python是對語法錯誤等錯誤進行定義和處理的,這包括至少有兩種可以區分的錯誤,它們是 語法錯誤 和 異常 。
語法錯誤
Python的語法錯誤就是不符合Python語法的錯誤,又稱為 解析錯誤 。這種錯誤是初學Python對語法不是很熟悉時經常犯的。比如下面的例子:
In [1]: if 2 == 3 print('imposible') File "<ipython-input-1-15af39b28602>", line 1 if 2 == 3 print('imposible') ^ SyntaxError: invalid syntax
直譯器在解釋上面的程式碼時,就會發現表示式 2 == 3
後面少了一個冒號 :
,這時它就會報出一個錯誤 SyntaxError: invalid syntax
,並且輸出出現語法錯誤的那一行,並顯示一個“箭頭”,指向這行裡面檢測到第一個錯誤。 錯誤是由箭頭指示的位置 上面 的 token 引起的(或者至少是在這裡被檢測出的)。檔名和行號也會被輸出,以便輸入來自指令碼檔案時你能知道去哪檢查。
直譯器這樣報出的好處是:
(1)告訴我們哪一行程式碼出錯了;
(2)錯誤的型別是什麼。
這樣非常有利於我們排除錯誤,修正程式。
異常(Exception)
如果我們對語法很熟悉,寫出來的程式碼在語法上都是正確的,但也不能保證在執行時程式不會引發錯誤。在執行時檢測到的錯誤被稱為 異常 ,異常不一定會導致嚴重後果,但我們不在程式碼中對它們進行處理,就可能會導致程式中斷執行。下面是一些常見的錯誤異常資訊:
In [2]: 5 / 0 ---------------------------- ZeroDivisionErrorTraceback (most recent call last) <ipython-input-2-adafc2937013> in <module> ----> 1 5 / 0 ZeroDivisionError: division by zero In [3]: a + 3 ------------------------------ NameErrorTraceback (most recent call last) <ipython-input-3-d390b6b495e8> in <module> ----> 1 a + 3 NameError: name 'a' is not defined In [4]: 10 + '1' -------------------------------- TypeErrorTraceback (most recent call last) <ipython-input-4-84c3fd9e0d8f> in <module> ----> 1 10 + '1' TypeError: unsupported operand type(s) for +: 'int' and 'str'
我們看到,異常有不同的型別,其型別名稱會作為錯誤資訊的一部分中打印出來,上述示例中的異常型別分別是: ZeroDivisionError
, NameError
和 TypeError
。對於所有內建異常,打印出來的字串是內建異常的名稱。對於使用者定義的異常則不一定如此,但我們自定義異常時最好按照內建異常那樣去定義,這是一個很有用的規範。標準的異常型別是內建的識別符號,而不是保留關鍵字。
打印出來的異常名稱後面是異常發生的原因。錯誤資訊的前一部分以堆疊回溯的形式顯示發生異常時程式碼的上下文。一般它包含列出原始碼行的堆疊回溯;但是它不會顯示從標準輸入中讀取的行。
Python內建了很多異常,它們都從 BaseException
繼承而來,下面是內建異常的繼承關係:

異常處理
既然程式會丟擲異常,那我們就可以編寫程式碼處理這些異常。先看下面的例子,它會讓使用者一直輸入,直到輸入的是一個有效的整數。我們也可以使用 Control-C
來中斷程式;這個 Control-C
引起的中斷會引發 KeyboardInterrupt 異常。
In [6]: while 1: ...:try: ...:n = int(input('input a number:')) ...:print('You typed number:', n) ...:break ...:except ValueError: ...:print('Nooo! It is not a number, Try agin') ...: input a number:a Nooo! It is not a number, Try agin input a number:b Nooo! It is not a number, Try agin input a number:3 You typed number: 3
當我們輸入 a
時,它不能轉換成整數就會報錯異常 ValueError
。轉換為整數的那條語句報出了異常,它後面的語句就不再執行,而是跳到 except
那裡去執行它裡面的語句。
try
語句的工作原理如下:
- 首先,執行 try 子句 ,即
try
和except
關鍵詞之間的(一行或多行)語句; - 如果沒有發生異常,則跳過 except子句 並完成
try
子句的執行; - 如果執行try子句是發生了異常,則跳過該子句的剩下部分。然後,去匹配異常的型別和
except
關鍵字後面的異常,如果異常型別匹配則執行except子句,之後繼續執行try語句後面的程式碼。 - 如果發生的異常和except後面的異常不匹配,則將其傳遞到外部的try語句,如果沒有找到處理程式碼,則它是一個
未處理異常
,執行將停止並顯示錯誤資訊。
一個 try
語句可以有多個except子句,以便不同的異常用不同的處理程式進行處理。每次遇到異常最多會執行一個except子句,也就是說,處理程式只處理相應的異常,而不處理同一try語句內其它處理程式的異常。但是,一個except子句可以將多個異常包含在一個元組內,例如:
try: ... except (RuntimeError, TypeError, NameError): pass
異常都是繼承於 BaseException
,如果except子句中的類和發生的異常是同一個類,或者是異常的基類(父類),則異常和except子句中的類是相容的。但是,反過來則不成立。我們看看下面的程式碼,它將一次列印B,C,D。
class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B")
如果我把 except 子句顛倒過來,把 except B 放到第一個,猜猜它將會打印出什麼?答案是它將列印 B,B,B。也就是第一個匹配的 except 子句被觸發,因為B是C、D的父類。
最後的 except 子句可以省略異常名稱,以用作萬用字元匹配所有的異常。這個要小心使用,因為這種方式很容易掩蓋真正的程式設計錯誤!但是它可用於列印錯誤訊息,然後重新引發異常(同樣允許呼叫者處理異常):
import sys try: f = open('zzz.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert string to integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
try 語句有一個可選的 else 子句,在使用時它必須放在所有的 except 子句後面。對於在try 子句不引發異常時必須執行的程式碼來說很有用。例如:
try: f = open('zzz.txt', 'r') except OSError: print('cannot open', 'zzz.txt') else: print('zzz.txt', 'has', len(f.readlines()), 'lines') f.close()
使用 else
子句的好處是,它避免了意外捕獲由else子句引發的異常。也就是說,程式中我們只想捕獲 open
引發的異常,而不捕獲 f.readlines()
引發的錯誤。
異常在丟擲時可能具有關聯的值,稱為異常引數。引數的存在和型別取決於異常型別。
except子句可以在異常名稱後面指定一個變數,這個變數就是該異常的例項,它的引數儲存在 instance.args
中。為了方便起見,異常例項定義了 __str__()
,因此可以直接列印引數而無需引用 .args
。也可以在丟擲之前首先例項化異常,並根據需要向其新增任何屬性。:
In [7]: try: ...:raise Exception('認真學', 'Python') ...: except Exception as e: ...:print(type(e)) ...:print(e.args) ...:print(e) ...:a, b = e.args ...:print('a =', a) ...:print('b =', b) ...: <class 'Exception'> ('認真學', 'Python') ('認真學', 'Python') a = 認真學 b = Python
異常處理程式不僅可以處理try子句中遇到的異常,還可以處理try子句中呼叫的函式的內部發生的異常,例如:
In [8]: def func(): ...:return 10/0 ...: In [9]: try: ...:func() ...: except ZeroDivisionError as err: ...:print('run-time error:', err) ...: run-time error: division by zero
大家在學python的時候肯定會遇到很多難題,以及對於新技術的追求,這裡推薦一下我們的Python學習扣qun:784758214,這裡是python學習者聚集地!!同時,自己是一名高階python開發工程師,從基礎的python指令碼到web開發、爬蟲、django、資料探勘等,零基礎到專案實戰的資料都有整理。送給每一位python的小夥伴!每日分享一些學習的方法和需要注意的小細節
