python 錯誤捕獲機制分析


python語言是編程中使用率在Top 3之內的語言。python語言以靈活與簡單著稱,那么越是靈活的語言越需要判斷出錯的功力。

 

簡單示例

以下是一個簡單的錯誤程序,被除數不可為0,那么看看該代碼的執行。

a = 10 

b = 0

c = a / b

print c 

出錯報告是:在文件error_three.py 的第5行 c = a / b報錯,原因是整數除法或者取模運算的被除數為0

解釋異常被捕獲的流程:當程序運行到 c = a / b時,有異常產生,然后python解釋器就捕獲了該異常,並判斷異常的類型,最后將該異常拋出,終止了程序的運行

python的異常捕獲原則是這樣的:程序某一行產生異常,解釋器會一層層的上報,尋找異常的捕獲代碼。如果解釋一直上報到main()函數中都沒有捕獲異常的語句,那么main()函數會捕獲到異常,並拋出異常。

 

下面看一個復雜一點的函數異常處理:

 1 #coding:utf-8
 2 
 3 def get_num():
 4     num = int(input("input you num:"))
return num
5 6 def fun_two(): 7 num = get_num() 8 div = 10 -num 9 return div 10 11 def fun_three(): 12 two_num = fun_two() 13 three_num = 10 / two_num 14 print three_num 15 16 17 if __name__ == '__main__': 18 fun_three()

 

這里產生異常的點有兩個:

1、被除數為0,在13行

2、輸入類型錯誤在4行

 

首先我們看第一個異常:

輸入10,div = 10 - 10 = 0,three_num = 10 / 0,然后報錯。

具體分析報錯信息,從上到下:

1、錯誤出現在文件error_two.py中的19行,出錯函數:fun_three

2、在fun_three中的14行,代碼:three_num = 10 / two_num中報錯,原因是:被除數不能為0。

按照python解釋器的工作原理:程序在14行出錯,然后一層層上報到main函數,main函數從上到下找到出錯點。下面看看第二個錯誤是不是也符合這個規律。

 

出錯解釋:

1、程序在19行出錯,出錯函數是:fun_three

2、fun_three內部在13行出錯,出錯代碼是函數:two_num = fun_two()

3、fun_two內部在8行出錯,出錯代碼是函數:num = get_num()

4、get_num函數在第4行出錯,出錯代碼是:num = int(input("input you num:")) 。出錯原因是:類型錯誤,int()函數不能轉化非數字類型的函數

 

從以上的分析來看,我們按照一個規律肯定能找到程序出錯的地方和原因。但是光找到出錯的地方還不夠,還要能夠自動的處理這些錯誤。程序要達到的效果是堅決不出錯誤,萬一出了錯誤也要能夠自動的捕獲錯誤,記錄下來。本着個原則,我們看看在python中是如果主動捕獲錯誤的。

 

以第一個簡單的程序為例,報錯如下:

 錯誤的確是顯示出來了,但是程序也停止了。這里有三點要求沒有達到:

1、程序終止了,后面如果還有重要代碼也無法執行了

2、程序是給大眾使用的,程序員肯定能看懂錯誤原因,但是普通人就不好說了。錯誤提示沒有

3、程序出錯也沒有錯誤日志記錄,當然前提是需要的話

 

捕獲異常

那么給這個簡單的小程序加一個異常捕獲的代碼:

 1 #coding:utf-8
 2 a = 10 
 3 
 4 b = 0
 5 try:
 6     c = a / b
 7 except:
 8     print "程序出現了異常"
 9 else:
10     print c

 

再次執行看看:

這樣異常就被except捕獲到了。並且捕獲到之后打印出except中的提示信息。

python中標准的程序異常捕獲語句是:

try
    可能出錯的語句
except:
    捕獲到的異常

 

一直提到異常,前面我們也見過了兩個異常,分別是:TypeError和ZeroDivisionError,這兩個異常說的是類型異常和被除數為0。聰明的讀者你肯定想到python已經定了常見的,大量的異常類型。是的,具體來說python定義的異常有如下:

 1 BaseException
 2  +-- SystemExit
 3  +-- KeyboardInterrupt
 4  +-- GeneratorExit
 5  +-- Exception
 6       +-- StopIteration
 7       +-- StopAsyncIteration
 8       +-- ArithmeticError
 9       |    +-- FloatingPointError
10       |    +-- OverflowError
11       |    +-- ZeroDivisionError
12       +-- AssertionError
13       +-- AttributeError
14       +-- BufferError
15       +-- EOFError
16       +-- ImportError
17            +-- ModuleNotFoundError
18       +-- LookupError
19       |    +-- IndexError
20       |    +-- KeyError
21       +-- MemoryError
22       +-- NameError
23       |    +-- UnboundLocalError
24       +-- OSError
25       |    +-- BlockingIOError
26       |    +-- ChildProcessError
27       |    +-- ConnectionError
28       |    |    +-- BrokenPipeError
29       |    |    +-- ConnectionAbortedError
30       |    |    +-- ConnectionRefusedError
31       |    |    +-- ConnectionResetError
32       |    +-- FileExistsError
33       |    +-- FileNotFoundError
34       |    +-- InterruptedError
35       |    +-- IsADirectoryError
36       |    +-- NotADirectoryError
37       |    +-- PermissionError
38       |    +-- ProcessLookupError
39       |    +-- TimeoutError
40       +-- ReferenceError
41       +-- RuntimeError
42       |    +-- NotImplementedError
43       |    +-- RecursionError
44       +-- SyntaxError
45       |    +-- IndentationError
46       |         +-- TabError
47       +-- SystemError
48       +-- TypeError
49       +-- ValueError
50       |    +-- UnicodeError
51       |         +-- UnicodeDecodeError
52       |         +-- UnicodeEncodeError
53       |         +-- UnicodeTranslateError
54       +-- Warning
55            +-- DeprecationWarning
56            +-- PendingDeprecationWarning
57            +-- RuntimeWarning
58            +-- SyntaxWarning
59            +-- UserWarning
60            +-- FutureWarning
61            +-- ImportWarning
62            +-- UnicodeWarning
63            +-- BytesWarning
64            +-- ResourceWarning

 

常見的異常類型如下:

AttributeError:屬性錯誤,特性引用和賦值失敗時會引發屬性錯誤

NameError:試圖訪問的變量名不存在

SyntaxError:語法錯誤,代碼形式錯誤

Exception:所有異常的基類,因為所有python異常類都是基類Exception的其中一員,異常都是從基類Exception繼承的,並且都在exceptions python 模塊中定義。

IOError:python ioerror,一般常見於打開不存在文件時會引發IOError錯誤,也可以解理為輸出輸入錯誤

KeyError:使用了映射中不存在的關鍵字(鍵)時引發的關鍵字錯誤

IndexError:索引錯誤,使用的索引不存在,常索引超出序列范圍

TypeError:類型錯誤,內建操作或是函數應於在了錯誤類型的對象時會引發類型錯誤

ZeroDivisonError:除數為0,在用除法操作時,第二個參數為0時引發了該錯誤

ValueError:值錯誤,傳給對象的參數類型不正確,像是給int()函數傳入了字符串數據類型的參數。

 

標准異常捕獲 

在上面的處理函數中我都是用異常的基類去捕獲異常的。這樣做有一個不好的地方就是我們雖然能捕獲異常,但是不能看出是哪一個異常。使用更精確的異常捕獲類型能夠精確找到出錯點。

 1 #coding:utf-8
 2 
 3 def get_num():
 4     try:
 5         num = int(input("input you num:"))
 6    except TypeError,e:
 7         print e
 8 
 9 def fun_two():
10     num = get_num()
11     div = 10 -num
12     return div
13 
14 def fun_three():
15     two_num = fun_two()
16     three_num = 10 / two_num 
17     print three_num
18 
19 
20 if __name__ == '__main__':
21     fun_three()

 

怎么還是報錯了,不是捕獲了異常了嗎?這里要看清楚,我是在輸入的時候捕獲的異常,並且異常也捕獲到了,看前兩行的輸出信息:int()函數的參數一定要是一個字符串或者數字,不能使type類型。按照之前的規律去分析一下這里的報錯在哪里。

1、報錯在21行,函數fun_three

2、fun_three函數中的15行報錯,two_num = fun_two()

3、fun_two函數中11行報錯,報錯代碼是:div = 10 - num,報錯信息是:TypeError,不支持int類型和空類型的變量相減

這里就是另一個錯誤了,輸入在前,相減在后。至少說明兩個問題:一、在輸入時精確捕獲到異常TypeError;二、捕獲異常后面的代碼能夠正常運行。那么我雖然捕獲到了異常,但程序還是異常終止了。原因在於捕獲的位置不是最佳位置。嘗試換一個位置來試試。

 

 1 #coding:utf-8
 2 
 3 def get_num():
 4     num = int(input("input you num:"))
 5     return num
 6 
 7 def fun_two():
 8     num = get_num()
 9     div = 10 -num
10     return div
11 
12 def fun_three():
13     try:
14         two_num = fun_two()
15         three_num = 10 / two_num 
16         print three_num
17     except (TypeError,ZeroDivisionError),e:
18         print e
19 
20 
21 if __name__ == '__main__':
22     fun_three()

 

將錯誤捕獲加在最后調用的地方,並且針對兩種可能出現的錯誤都做了捕獲。那么這里理論上就能捕獲兩個可能出現的錯誤。我們看看結果如何:

可以看到兩種錯誤都能夠捕獲到,並且沒有報出異常信息。以python解釋器錯誤拋出的規則來分析:

一、當輸入為字符串“str”時,程序報錯,然后解釋器一層層向上尋找異常捕獲的語句;

二、在get_num()函數中沒有,向上尋找調用它的函數fun_two(),任然沒有;

三、繼續向上尋找調用fun_two的函數fun_three,在fun_three中尋找到捕獲異常的語句except (TypeError,ZeroDivisionError),e

四、捕獲到異常之后拋出異常。

 

python標准出錯處理

在編碼中常用的出錯處理機制是:

try:
    可能出錯的地方
except:
    錯誤捕獲,出錯時要執行的代碼
else:
    沒有錯誤時要執行代碼
finally:
    不管有沒有錯誤都要執行的代碼

這種出錯處理適用於不管有沒有出錯,都一定要執行某些操作。常見的是打開了文件,不管是否打開成功,都要關閉。

例如:

 

 1 #coding:utf-8
 2 
 3 try:
 4     f = open('file.txt','w')
 5     print f.read()
 6     f.close()
 7 except IOError,e:
 8     print e
 9 else: 
10     print f.read()
11 finally:
12     if f:
13         f.close()

 

以寫方式打開一個文件,但是卻有讀操作,這樣就會報錯。雖然按照文件處理規范,在打開之后也添加了關閉文件的代碼,報錯之后的代碼不會執行,所有這個錯誤會被except IOError捕獲。但是捕獲之后打開的文件並沒有關閉,文件連接是消耗資源的事件,那么在finally中就要關閉文件連接。

 

 合適的錯誤捕獲能夠增加程序的健壯性,是在編碼中一定要能夠熟練使用的技巧

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM