天天看點

Python3 錯誤和異常

文法錯誤

異常

異常處理

抛出異常

使用者自定義異常

finally語句

預定義的清理行為

異常處理機制概述: 

異常處理,是程式設計語言或計算機硬體裡的一種機制,用于處理軟體或資訊系統中出現的異常狀況(即超出程式正常執行流程的某些特殊條件)。通過異常處理,我們可以對使用者在程式中的非法輸入進行控制和提示,以防程式崩潰。 

就好比一個旅遊景點,每到一個有可能出現問題情況的地方就會設定一個處理問題的處理點,不同的問題有不同的處理點,例如花粉過敏有花粉過敏的處理點,摔傷有摔傷的處理點等。程式也是如此會出現各種各樣的錯誤,同理不同的異常錯誤有不同的異常錯誤處理方法。 

各種程式設計語言在處理異常方面具有非常顯著的不同點(錯誤檢測與異常處理差別在于:錯誤檢測是在正常的程式流中,處理不可預見問題的代碼,例如一個調用操作未能成功結束)。某些程式設計語言有這樣的函數:當輸入存在非法資料時不能被安全地調用,或者傳回值不能與異常進行有效的差別。例如,C語言中的atoi函數(ASCII串到整數的轉換)在輸入非法時可以傳回0。在這種情況下程式設計者需要另外進行錯誤檢測(可能通過某些輔助全局變量如C的errno),或進行輸入檢驗(如通過正規表達式),或者共同使用這兩種方法。 

在python中我們可以通過try-except語句來捕捉異常,文法錯誤的話開發工具都會有提示的。

文法錯誤或者稱之為解析錯誤,是初學者經常碰到的,如下執行個體:

1

2

3

4

5

<code>&gt;&gt;&gt; </code><code>while</code> <code>True</code> <code>print</code><code>(</code><code>'Hello world'</code><code>)</code>

<code>  </code><code>File</code> <code>"&lt;stdin&gt;"</code><code>, line </code><code>1</code><code>, </code><code>in</code> <code>?</code>

<code>    </code><code>while</code> <code>True</code> <code>print</code><code>(</code><code>'Hello world'</code><code>)</code>

<code>                   </code><code>^</code>

<code>SyntaxError: invalid syntax</code>

這個例子中,函數 print() 被檢查到有錯誤,是它前面缺少了一個冒号( : )。 

解釋器會指出了出錯的一行,并且在最先找到的錯誤的位置标記了一個小小的箭頭。

即便代碼的文法是正确的,但是在運作它的時候,也有可能發生錯誤。運作期間檢測到的錯誤被稱為異常,例如不能被0整除錯誤,或者空指針異常。 

大多數的異常都不會被程式處理,都以錯誤資訊的形式展現在這裡:

6

7

8

9

10

11

12

<code>&gt;&gt;&gt; </code><code>10</code> <code>*</code> <code>(</code><code>1</code><code>/</code><code>0</code><code>)   </code><code># 不能被0整除異常</code>

<code>Traceback (most recent call last):</code>

<code>ZeroDivisionError: division by zero</code>

<code>&gt;&gt;&gt; </code><code>4</code> <code>+</code> <code>spam</code><code>*</code><code>3</code>

<code>NameError: name </code><code>'spam'</code> <code>is</code> <code>not</code> <code>defined   </code><code># 變量未聲明異常</code>

<code>&gt;&gt;&gt; </code><code>'2'</code> <code>+</code> <code>2</code>  <code># 類型異常</code>

<code>TypeError: Can</code><code>'t convert '</code><code>int</code><code>' </code><code>object</code> <code>to </code><code>str</code> <code>implicitly</code>

異常會有不同的類型,這些類型都作為資訊的一部分列印出來,以上例子中的類型有 ZeroDivisionError(不能被0整除異常),NameError( 變量未聲明異常) 和 TypeError(類型異常),這些異常類型能提示開發人員發生的是什麼樣的異常,這樣就可以分析錯誤發生在何處。 

錯誤資訊的前面部分顯示了異常發生的上下文,并以調用棧的形式顯示具體資訊。

在python中通過try-except語句來處理異常,例如将可能會出現不能被0整除異常的代碼寫在try代碼塊裡,try代碼塊裡的代碼執行過程中出現異常後,就會執行except代碼塊裡的代碼,代碼示例:

<code># 會出現異常的代碼寫在try裡</code>

<code>try</code><code>:</code>

<code>    </code><code>num</code><code>=</code><code>10</code><code>/</code><code>0</code>

<code>    </code><code>print</code><code>(num)  </code><code># 如果上面的代碼出現異常這句代碼不會被執行</code>

<code>except</code><code>:</code>

<code>    </code><code>print</code><code>(</code><code>"出現異常了!"</code><code>)   </code><code># try裡的代碼出現異常後就會執行這裡的代碼</code>

運作結果:

出現異常了!

try語句按照如下方式工作;

首先,執行try子句(在關鍵字try和關鍵字except之間的語句)

如果沒有異常發生,會忽略except子句,try子句執行後就結束。

如果在執行try子句的過程中發生了異常,那麼try子句餘下的部分将被忽略。如果異常的類型和 except 之後的名稱相符,那麼對應的except子句将被執行。最後執行 try 語句之後的代碼。

如果一個異常沒有與任何的except比對,那麼這個異常将會被抛出。

一個 try 語句可以包含多個except子句,分别來處理不同的特定的異常。但是最多隻有一個分支會被執行。

以上示例的是最簡單的使用方式,能夠捕獲所有類型的異常,稱之為通用異常陷阱。如果需要捕捉特定的異常,可以在except中聲明異常的類型,那麼這個陷阱就隻能捕獲你所聲明的異常類型,但是可以在末尾寫上一個通用異常陷阱,沒有被特定的陷阱所捕獲的異常最後就會被通用異常陷阱所捕獲。如果你在except中聲明了一個異常類型,可以通過as關鍵字指派給一個變量,通過這個變量可以列印出錯誤資訊,代碼示例:

<code>    </code><code>num </code><code>=</code> <code>10</code> <code>/</code> <code>0</code>

<code>    </code><code>print</code><code>(num)</code>

<code>except</code> <code>ZeroDivisionError as err: </code><code># 指派給err變量</code>

<code>    </code><code>print</code><code>(</code><code>"出現異常:"</code><code>, err)</code>

<code>except</code> <code>TypeError:  </code><code># 聲明一個指定的異常類型</code>

<code>    </code><code>print</code><code>(</code><code>"出現類型異常!"</code><code>)</code>

<code>except</code><code>:    </code><code># 末尾可以使用一個通用異常</code>

<code>    </code><code>print</code><code>(</code><code>"出現異常了!"</code><code>)</code>

出現異常: division by zero

一個except子句可以同時處理多個異常,這些異常将被放在一個括号裡成為一個元組,例如:

except (RuntimeError, TypeError, NameError):

使用raise關鍵字,可以将異常再次抛出,會抛出到解釋器中,代碼示例:

<code>    </code><code>raise</code>

<code>出現異常: division by zero</code>

<code>  </code><code>File</code> <code>"E:/PythonProject/TestExcept.py"</code><code>, line </code><code>2</code><code>, </code><code>in</code> <code>&lt;module&gt;</code>

try except 語句還有一個可選的else子句,這個子句必須放在所有的except子句之後。這個子句将在try子句沒有發生任何異常的時候執行。例如:

<code>    </code><code>num </code><code>=</code> <code>1</code><code>+</code><code>1</code>

<code>else</code> <code>:</code>

<code>    </code><code>print</code><code>(</code><code>"沒有出現異常!"</code><code>)</code>

沒有出現異常!

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

異常處理并不僅僅處理那些直接發生在try子句中的異常,而且還能處理子句中調用的函數(甚至間接調用的函數)裡抛出的異常。例如:

13

<code>def</code> <code>errorTest():</code>

<code>    </code><code>return</code> <code>1</code><code>/</code><code>0</code>

<code>    </code><code>num </code><code>=</code> <code>errorTest()</code>

上面示例也用到了raise 關鍵字,通過這個關鍵字可以抛出異常到外部。也可以使用此關鍵字在代碼中抛出特定的異常,如果這個關鍵字寫在except裡,并且沒有指定要抛出的異常,那麼這個raise 就會抛出這個陷阱裡的異常,代碼示例:

<code>except</code> <code>ZeroDivisionError:</code>

<code>  </code><code>File</code> <code>"E:/PythonProject/TestExcept.py"</code><code>, line </code><code>3</code><code>, </code><code>in</code> <code>&lt;module&gt;</code>

使用raise 關鍵字抛出指定的異常示例:

<code>i</code><code>=</code><code>0</code>

<code>j</code><code>=</code><code>1</code>

<code>if</code> <code>i!</code><code>=</code><code>0</code><code>:</code>

<code>    </code><code>k</code><code>=</code><code>j</code><code>/</code><code>i</code>

<code>else</code><code>:</code>

<code>    </code><code>print</code><code>(</code><code>"抛出一個異常:"</code><code>)</code>

<code>    </code><code>raise</code> <code>ZeroDivisionError</code>

<code>  </code><code>File</code> <code>"E:/PythonProject/TestExcept.py"</code><code>, line </code><code>8</code><code>, </code><code>in</code> <code>&lt;module&gt;</code>

<code>ZeroDivisionError</code>

抛出的異常可以指定一個字元串類型的參數,這個參數也會随着異常資訊列印出來,代碼示例:

<code>    </code><code>raise</code> <code>ZeroDivisionError(</code><code>"我是異常"</code><code>)</code>

<code>抛出一個異常:</code>

<code>  </code><code>File</code> <code>"E:/PythonProject/TestExcept.py"</code><code>, line </code><code>9</code><code>, </code><code>in</code> <code>&lt;module&gt;</code>

<code>ZeroDivisionError: 我是異常</code>

你可以通過建立一個新的exception類來擁有自己的異常。異常應該繼承自 Exception 類,或者直接繼承,或者間接繼承,例如:

14

15

16

17

18

19

20

<code># 這是一個自定義的異常類</code>

<code>&gt;&gt;&gt; </code><code>class</code> <code>MyError(Exception):  </code><code># 繼承于Exception類</code>

<code>        </code><code>def</code> <code>__init__(</code><code>self</code><code>, value):  </code><code># 這是初始化方法,也就是構造器</code>

<code>            </code><code>self</code><code>.value </code><code>=</code> <code>value  </code><code>#這是這個類的屬性</code>

<code>        </code><code>def</code> <code>__str__(</code><code>self</code><code>):   </code><code># 類中的每個方法都需要有一個self參數,通過這個參數來擷取類屬性的值</code>

<code>            </code><code>return</code> <code>repr</code><code>(</code><code>self</code><code>.value)</code>

<code>&gt;&gt;&gt; </code><code>try</code><code>:</code>

<code>        </code><code>raise</code> <code>MyError(</code><code>2</code><code>*</code><code>2</code><code>)  </code><code>#抛出自定義的異常類</code>

<code>    </code><code>except</code> <code>MyError as e:</code>

<code>        </code><code>print</code><code>(</code><code>'My exception occurred, value:'</code><code>, e.value)  </code><code>#會被這裡捕獲</code>

<code>My exception occurred, value: </code><code>4</code>   <code># 運作結果</code>

<code>&gt;&gt;&gt; </code><code>raise</code> <code>MyError(</code><code>'oops!'</code><code>)  </code><code># 抛出自定義的異常類</code>

<code># 列印的異常資訊</code>

<code>__main__.MyError: </code><code>'oops!'</code>

在這個例子中,類 Exception 預設的 _init_( ) 被覆寫。 

當建立一個子產品有可能抛出多種不同的異常時,一種通常的做法是為這個包建立一個基礎異常類,然後基于這個基礎類為不同的錯誤情況建立不同的子類:

21

22

23

24

25

26

27

28

29

30

<code>class</code> <code>Error(Exception):</code>

<code>    </code><code>"""Base class for exceptions in this module."""</code>

<code>    </code><code>pass</code>

<code>class</code> <code>InputError(Error):</code>

<code>    </code><code>"""Exception raised for errors in the input.</code>

<code>    </code><code>Attributes:</code>

<code>        </code><code>expression -- input expression in which the error occurred</code>

<code>        </code><code>message -- explanation of the error</code>

<code>    </code><code>"""</code>

<code>    </code><code>def</code> <code>__init__(</code><code>self</code><code>, expression, message):</code>

<code>        </code><code>self</code><code>.expression </code><code>=</code> <code>expression</code>

<code>        </code><code>self</code><code>.message </code><code>=</code> <code>message</code>

<code>class</code> <code>TransitionError(Error):</code>

<code>    </code><code>"""Raised when an operation attempts a state transition that's not</code>

<code>    </code><code>allowed.</code>

<code>        </code><code>previous -- state at beginning of transition</code>

<code>        </code><code>next -- attempted new state</code>

<code>        </code><code>message -- explanation of why the specific transition is not allowed</code>

<code>    </code><code>def</code> <code>__init__(</code><code>self</code><code>, previous, </code><code>next</code><code>, message):</code>

<code>        </code><code>self</code><code>.previous </code><code>=</code> <code>previous</code>

<code>        </code><code>self</code><code>.</code><code>next</code> <code>=</code> <code>next</code>

大多數的異常的名字都以”Error”結尾,就跟标準的異常命名一樣。

try 語句還有另外一個可選的子句,這個語句無論在任何情況下都會執行,也就是所謂的最終執行塊,這個代碼塊裡的代碼不管什麼有沒有發生異常都會被執行,一般用于執行close之類的關閉資源的語句。 例如:

<code>    </code><code>filedata </code><code>=</code> <code>open</code><code>(</code><code>"E:/test.txt"</code><code>)</code>

<code>except</code> <code>IOError:</code>

<code>    </code><code>print</code><code>(</code><code>"檔案打開失敗!"</code><code>)</code>

<code>finally</code><code>:</code>

<code>    </code><code>filedata.close()</code>

<code>    </code><code>print</code><code>(</code><code>"資源已關閉!"</code><code>)</code>

資源已關閉!

以上例子不管 try 子句裡面有沒有發生異常,finally 子句都會執行。 

如果一個異常在 try 子句裡(或者在 except 和 else 子句裡)被抛出,而又沒有任何的 except 把它截住,那麼這個異常會在 finally 子句執行後再次被抛出。 

代碼示例:

<code>資源已關閉!</code>

一些對象定義了标準的清理行為,無論系統是否成功的使用了它,一旦不需要它了,那麼這個标準的清理行為就會執行。 

這面這個例子展示了嘗試打開一個檔案,然後把内容列印到螢幕上:

<code>for</code> <code>line </code><code>in</code> <code>open</code><code>(</code><code>"myfile.txt"</code><code>):</code>

<code>    </code><code>print</code><code>(line, end</code><code>=</code><code>"")</code>

以上這段代碼的問題是,當執行完畢後,檔案會保持打開狀态,并沒有被關閉。 

之前介紹過的關鍵詞 with 語句就可以保證諸如檔案之類的對象在使用完之後一定會正确的執行他的清理方法,這種就是預定義的清理行為:

<code>with </code><code>open</code><code>(</code><code>"myfile.txt"</code><code>) as f:</code>

<code>    </code><code>for</code> <code>line </code><code>in</code> <code>f:</code>

<code>        </code><code>print</code><code>(line, end</code><code>=</code><code>"")</code>

以上這段代碼執行完畢後,就算在處理過程中出問題了,檔案 f 也會關閉。

本文轉自 ZeroOne01 51CTO部落格,原文連結:http://blog.51cto.com/zero01/1981049,如需轉載請自行聯系原作者

繼續閱讀