Python回顧與整理8:錯誤和異常

0.說明
python


        若是想寫出用戶體驗高的代碼,那麼就須要考慮到在執行本身寫的這段代碼中在和用戶交互的過程當中可能會出現的問題,也就是說,須要對可能出現的異常進行處理,只有作好這些工做,才能寫出用戶體驗好的代碼。數據庫




1.什麼是異常express


  • 錯誤編程

        錯誤是語法(致使解釋器沒法解釋)或邏輯(也就是代碼質量問題)上的,在Python中,當檢測到錯誤時,解釋器會指出當前流沒法繼續執行下去,因而就出現了異常。bash

  • 異常app

        程序出現了錯誤而在正常控制流之外採起的行爲。
python2.7

        根據上面的解釋,能夠理解爲,只要解釋器檢測到程序運行時出現了錯誤(與Python解釋器不相容而致使),就會觸發一個異常。
ide




2.Python中的異常函數


        以下:
工具

異常類型 描述 簡單例子
NameError 嘗試訪問一個未聲明的變量,或者是在名稱空間中不存在的變量
>>> xpleaf
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xpleaf' is not defined
ZeroDivisionError 除數爲零
>>> 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division
 or modulo by zero
SyntaxError

Python解釋器語法錯誤

(惟一不是在運行時發生的異常,發生在編譯時,Python解釋器沒法把相關腳本編譯爲Python字節代碼)

>>> for
  File "<stdin>", line 1
    for
      ^
SyntaxError: invalid syntax
IndexError 請求的索引走出序列範圍
>>> aList = []
>>> aList[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
KeyError 請求一個不存在的字典關鍵字
>>> aDict = {'name': 'xpleaf', 'love': 'cl'}
>>> aDict['clyyh']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'clyyh'
IOError

輸入/輸出錯誤

(任何類型的I/O錯誤都會引起IOError異常)

>>> f = open('xpleaf')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or 
directory: 'xpleaf'
AttributeError 嘗試訪問未知的對象屬性
>>> class myClass(object):
...   pass
... 
>>> myInst = myClass()
>>> myInst.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'myClass' object has no 
attribute 'name'




3.檢測和處理異常


        須要注意的是,這和前面提到的檢測和處理錯誤並不同,檢測和處理錯誤的結果是會引起一個異常,這是由Python解釋器完成的;固然咱們也能夠人爲地觸發一個異常,這時開發者會認爲,用戶對程序的使用是不正確的,因此才引起這樣一個異常。

        當異常出現的時候,若是不對該異常進行處理,那麼Python解釋器就會停止當前程序的運行,所以,咱們須要對異常進行處理,以達到即便異常出現了,也不會停止程序的執行。


(1)try-except語句

  • 語法

try:
    try_suite    #監測這裏的異常
except Exception[, reason]:
    except_suit    #異常處理代碼

        reason是錯誤緣由,由捕獲的異常自己帶有,只須要定義一個變量便可以對其進行使用。

        打開一個不存在的文件時:

>>> f = open('xpleaf')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'xpleaf'

        其中:

[Errno 2] No such file or directory: 'xpleaf'

        即是錯誤緣由,可使用try-except語句來處理上面的異常:

>>> try:
...   f = open('xpleaf', 'r')
... except IOError, e:
...   print 'could not open file:', e
... 
could not open file: [Errno 2] No such file or directory: 'xpleaf'

忽略代碼,繼續執行,向上移交:

指的是,若是該層代碼(好比一個函數內)有相關的異常處理器(即except語句),就會跳到該異常處理器中進行處理,後面的代碼會被忽略(後面的其它except語句);若是在該層沒有找到對應的異常處理器,該異常會被向上移交,好比移交到調用該函數的上層代碼;當異常到達最頂層仍然沒有找到對應處理器時,就認爲這個異常是未處理的,Python解釋器會顯示出跟蹤記錄,而後退出。


(2)帶有多個except的try語句

  • 語法

try:
    try_suite
except Exception1[, reason1]:
    suite_for_exception_Exception1
except Exception2[, reason2]:
    suite_for_exception_Exception2

        須要注意的是,當有異常發生時,一旦找到對應的異常處理器,程序的執行流就會跳轉到該異常處理器中,其它的except語句將會被忽略。


(3)處理多個異常的except語句

  • 語法

try:
    try_suite
except (Exception1, Exception2)[, reason1]:
    suite_for_exception_Exception1_and_Exception2

        須要注意的是,這些不一樣的異常應該被放入到一個元組中。

拓展:包裝內建函數

以下:

>>> def safe_float(obj):
...   try:
...     retval = float(obj)
...   except (ValueError, TypeError):
...     retval = 'argument must be a number or numeric string'
...   return retval

執行以下:

>>> safe_float(123)
123.0
>>> safe_float('123')
123.0
>>> safe_float('foo')
'argument must be a number or numeric string'

這是一種很是不錯的技巧,要善於利用。


(4)捕獲全部異常


        若是須要捕獲全部因錯誤而引發的異常,能夠直接捕獲Exception異常,Exception是絕大多數Python內建異常的基類。

        可是對於SystemExit和KeyboardInterupt這兩個異常,使用Exception是沒法捕獲的,由於它們不是Exception的繼承者,緣由很簡單,由於這兩個異常不是因爲錯誤條件引發的。SystemExit是因爲當前Python應用程序須要退出,KeyboardInterrupt表明用戶按下了ctrl-c,想要關閉Python。

        可是這三者都有一個共同的基類,那就是BaseException,也就是這三者在程序結構上是同級的,以下:

BaseException
  -KeyboardInterrupt
  -SystemExit
  -Exception
    -(all other current built-in exceptions)

        所以,若是真的想要捕獲全部的異常(包括非錯誤條件引發的),就可使用BaseException,能夠看下面的例子:

  • 使用Exception:沒法捕獲KeyboardInterrupt

        代碼以下:

try:
    name = raw_input('Your name:')
except Exception:
    print 'quit'

        執行以下:

/usr/bin/python2.7 /home/xpleaf/PycharmProjects/Python_book/10/test.py
Your name:Traceback (most recent call last):
  File "/home/xpleaf/PycharmProjects/Python_book/10/test.py", line 3, in <module>
    name = raw_input('Your name:')
KeyboardInterrupt
  • 使用BaseException:捕獲全部異常(錯誤與非錯誤條件引發的)

        代碼以下:

try:
    name = raw_input('Your name:')
except BaseException:
    print 'quit'

        執行以下:

/usr/bin/python2.7 /home/xpleaf/PycharmProjects/Python_book/10/test.py
Your name:quit

        這樣的好處是,若是須要同時捕獲三個同級的異常,使用一個except語句就能夠。


        可是須要注意的是,try-except語句是爲了更好地跟蹤潛在的錯誤並在代碼裏準備好處理異常的邏輯,不該該將其做爲異常過濾器來捕獲全部異常,並忽略掉這些異常。


(5)異常參數

        其實所謂異常參數,對於前面的一個例子,爲何使用e錯誤緣由時,就能夠獲得與該異常相關的字符串信息呢?那是由於,異常引起後,它傳遞了一個參數給異常處理器。

        直接看下面一個例子:

>>> try:
...     float('foo')
... except ValueError, e:
...     print 'Error Happen:', e
... 
Error Happen: could not convert string to float: foo
>>> 
>>> type(e)
<type 'exceptions.ValueError'>
>>> str(e)
'could not convert string to float: foo'
>>> print e
could not convert string to float: foo
>>> e.__class__
<type 'exceptions.ValueError'>
>>> e.__class__.__name__
'ValueError'
>>> e
ValueError('could not convert string to float: foo',)

        咱們能夠得出下面的結論:

  • 異常引起時,若是使用錯誤緣由變量,實際上,這是一個包含來自致使異常的診斷信息的類實例,異常參數自身會組成一個元組,並存儲爲這個異常類的屬性

        在這個例子中的分析是,引起了ValueError異常,而後e就是該異常的一個實例,而且在生成這個實例e的過程當中,異常參數('could not convert string to float: foo',)(注意這是一個元組),就會成爲e的一個屬性,而使用str(e)能夠輸出診斷信息的字符串,那是由於調用了該類實例的__str__()方法 。

        注意,若是用一個except語句來同時捕獲多個異常時,使用一個錯誤緣由便可,由於每個異常都會生成本身的異常參數。

        再強調:

  • 異常參數是該異常發生時傳遞給異常處理器的一個字符串對象,它會成爲這個異常類的實例的一個屬性,而且能夠經過調用str()來得到該診斷信息(使用print語句,實際也是調用了該str()方法)

拓展:繼續前面的float()例子

代碼以下:

def safe_float(object):
    try:
        retval = float(object)
    except (ValueError, TypeError), diag:
        retval = str(diag)
    return retval

result = safe_float('foo')
print result
result2 = safe_float([])
print result2

執行以下:

/usr/bin/python2.7 /home/xpleaf/PycharmProjects/Python_book/10/test.py
could not convert string to float: foo
float() argument must be a string or a number

        PS:更進一步學習,能夠考慮參考異常類的源代碼。


(6)else子句

        沒有捕獲到異常時,就執行else子句中的代碼塊,一個簡單的例子以下:

>>> try:
...     float(4)
... except (ValueError, TypeError), e:
...     print 'Error Happen:', e
... else:
...     print 'No Exceptions'
... 
4.0
No Exceptions


(7)finally子句

        即不管異常是否有發生或是否捕捉到異常,都會執行的語句塊。

        經常使用的方式以下:

  • try-except-finally

try:
    A
except Exception1, e:
    B
finally:
    C
  • try-except-else-finally

try:
    A
except Exception1, e:
    B
else:
    C
finally:
    D

        至於書本上說的各類形式上的問題,則能夠不用考慮太多,在實踐中使用時加以使用把可能出現的狀況考慮到就能夠了。




4.上下文管理


        try-except和try-finally的一種特定的用法是保證共享的資源的惟一分配,並在任務結束的時候釋放它,好比文件、線程資源、簡單同步、數據庫鏈接等,以打開文件爲例。但其實若是用with語句,會方便不少:

>>> with open('xpleaf.txt', 'r') as f:
...     for eachLine in f:
...         print eachLine
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'xpleaf.txt'

        with語句幫咱們作了不少事情:試圖打開一個文件,若是一切正常,把文件對象賦值給f.而後用迭代器遍歷文件中的每一行,當完成時,關閉文件,不管在這一段代碼的開始、中間仍是結束時發生異常,會執行清理的代碼,此外文件仍會被自動的關閉。

        固然這種方法僅適用於支持上下文管理協議的對象。關於上下文管理協議,因爲目前尚未使用到,因此暫不作總結。




5.字符串做爲異常


        知道有這種狀況就能夠,在實際中仍然使用類異常。




6.觸發異常


        使用raise關鍵字就能夠人爲地觸發各類異常。

  • 語法

raise [SomeException [, args [, traceback]]]

        其用法能夠有以下:

raise語句的用法
raise語法 描述
raise exclass 觸發一個異常,從cxclass生成一個實例(不含任何異常參數)
raise exclass() 同上,但如今不是類;經過函數調用操做符(其實就是指加上了`()`)做用於類生成一個新的exclass實例,一樣也沒有異常參數
raise exclass, args 同上,但同時提供的異常參數args,能夠是一個參數也能夠是元組
raise exclass(args) 同上
raise exclass, args, tb 同上,但提供一個跟蹤記錄(traceback)對象tb供使用
raise exclass, instance 經過實例觸發異常(一般是exclass的實例);若是實例是exclass的子類實例,那麼這個新異常的類型會是子類的類型(而不是exclass);若是實例既不是exclass的實例也不是exclass子類的實例,那麼會複製此實例爲異常參數去生成一個新的exclass實例
raise instance
經過實例觸發異常:異常類型是實例的類型;等價於raise instance.__class__, instance(同上)
raise 從新觸發前一個異常,若是以前沒有異常,觸發TypeError

        對於raise string以及相關的方法,這裏就不說起了,由於實際上不多用到,另外對於traceback也不經常使用。可舉例以下:

  • raise exclass

>>> raise ValueError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError
  • raise exclass()

>>> raise ValueError()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError
  • raise exclass, args

>>> raise ValueError, 'Something wrong happen about value'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
>>> 
>>> raise ValueError, ('New Error', 'Something wrong happen about value')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: ('New Error', 'Something wrong happen about value')
  • raise exclass(args)

>>> raise ValueError('Something wrong happen about value')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
  • raise exclass, instance

>>> newError = ValueError('Something wrong happen about value')
>>> raise ValueError, newError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
>>> 
>>> newError = ValueError('Something wrong happen about value')
>>> raise IOError, newError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: Something wrong happen about value
# 注意看異常類型和異常參數
  • raise instance

>>> newError = ValueError('Something wrong happen about value')
>>> raise newError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
>>> 
>>> raise newError.__class__, newError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
  • raise

>>> raise
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType
# 即達不到所描述的效果,即便前面已經有異常出現,仍是會觸發TypeError異常




7.斷言


        斷言經過assert語句實現,測試一個表達式,若是返回值是假,觸發異常。觸發異常時,能夠像處理普通異常同樣對它進行處理。

  • 語法

assert expression[, arguments]

        舉例以下:

>>> assert 1 == 1
>>> assert 1 == 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
>>>
>>> assert 1 == 0, 'One does not equal zero silly!'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: One does not equal zero silly!




8.標準異常


        全部的標準異常都是內建的,因此能夠直接在交互器或執行腳本文件時使用,關於Python當前的標準異常集,其實只要查看源代碼就能夠很清晰地知道有哪些標準異常了,這裏就再也不列出來了。

        另外,有3個直接從BaseException派生的異常子類:

  • SystemExit

  • KeyboardInterrupt

  • Exception

        其它的全部內建異常都是Exception的子類。




9.建立異常


        其實建立異常,只須要繼承一個異常,並根據本身的須要進行定製便可,但因爲目前還使用不到,因此先略過,實際上能夠經過書上的例子和異常類的源代碼來加深對Python面向對象編程的理解,日後再作整理。




10.(如今)爲何用異常


        確定是須要用異常的,由於須要達到這樣的目的:運行環境必須足夠強健,來處理應用級別的錯誤,並提供用戶級別的錯誤信息。這樣才能提供良好的用戶體驗。




11.到底爲何要異常


        沒有異常,將會致使不少問題。

        



12.異常和sys模塊


        能夠經過sys模塊中的exc_info()函數來獲取異常信息,舉例以下:

>>> try:
...     float('abc123')
... except:
...     import sys
...     exc_tuple = sys.exc_info()
... 
>>> print exc_tuple
(<type 'exceptions.ValueError'>, ValueError('could not convert string to float: abc123',), <traceback object at 0x7f9ba0fa0f38>)

        能夠看到,從sys.exc_info()中獲得一個三元組,元素分別以下:

  • exc_type:異常類

  • exc_value:異常類的實例

  • exc_traceback:跟蹤記錄對象

        跟蹤記錄對象提供了發生異常的上下文,包含諸如代碼的執行幀,異常發生時的行號等信息。




13.相關模塊


        以下:

異常相關的標準庫
模塊 描述
exceptions 內建異常(不須要導入這個模塊)
contextlib 爲使用with語句的上下文對象工具
sys 主要是sys.exc_info()
相關文章
相關標籤/搜索