1. 程式人生 > >Python錯誤、除錯和測試

Python錯誤、除錯和測試

一、錯誤處理
高階語言都內建了一套“try…..except…..finally”錯誤處理機制。
①try機制

try:
    r=12/0
    print ('result:',r)
except ZeroDivisionEror,e:   #e可用其他字元代替,表示一個變數,在這裡e=='inter division or modulo by zero'
    print 'except:',e
finally:#finnally可有可無,無論程式碼是否有錯誤都會執行
    print 'finally....'
print 'end'

上面程式碼在計算12/0時會產生一個除法運算錯誤:
except: integer division or modulo by zero
finally….
end
當發生錯誤時不再執行print ‘result:’,r
若沒有錯誤發生,except語句不會執行;另外錯誤會有不同種類,所以可以有多個不同的except語句;如果沒有錯誤發生,可以在except語句後加一個else語句,沒有錯誤發生就可以執行該語句。另:所有的錯誤型別都繼承自BaseException,所以可以用一個expect StandardError,e: 語句,省去用多個except語句去捕獲錯誤。
使用try….expect 可以跨越多層呼叫,呼叫函式,函式巢狀無論在哪一層出錯都可以捕獲到比如下例:

    def foo(s)
        return 10 / int(s)
    def bar(s):
        return foo(s) * 2
    def main():
    try:
        bar('0')
    except StandardError, e:
        print 'Error!'
    finally:
        print 'finally...'
    main()

輸出:

    Error integer division or modulo by zero

可見錯誤出現在foo()函式中,在函式呼叫中main()函式仍可以捕獲到
②呼叫堆疊
如果錯誤為被找到,則會列印錯誤資訊

  def f1(s):
      return 12/int(s)
  def f2(s):
      return f1(s)*2
  def main():
      f2('0')
  main()    

執行:結果如下

  Traceback (most recent call last):
  File "F:/python 程式碼/錯誤處理.py", line 7, in <module>
    main()
  File "F:/python 程式碼/錯誤處理.py", line 6, in main
    f2('0')
  File "F:/python 程式碼/錯誤處理.py", line 4, in f2
    return f1(s)*2
  File "F:/python 程式碼/錯誤處理.py", line 2, in f1
    return 12/int(s)
ZeroDivisionError: integer division or modulo by zero

可以看出,出現錯誤的地方為:

File "F:/python 程式碼/錯誤處理.py", line 2, in f1
return 12/int(s)

③記錄錯誤
python內建的logging模組可以記錄錯誤,把錯誤堆疊打印出來,同時讓程式繼續執行下去。

        import logging    #先匯入
        def f1(s):
            return 12/int(s)
        def f2(s):
            return f1(s)*2
        def main():
        try:
            f2('0')
        except StandardError,e:   #增加的步驟
            logging.exception(e)
        main()
        print('End') 

於上個程式碼相比匯入import,使用try…except。雖然同樣出錯,但列印完錯誤資訊後會繼續執行,並正常退出,執行程式碼如下:

ERROR:root:integer division or modulo by zero
Traceback (most recent call last):
  File "F:/python 程式碼/錯誤處理.py", line 8, in main
    f2('0')
  File "F:/python 程式碼/錯誤處理.py", line 5, in f2
    return f1(s)*2
  File "F:/python 程式碼/錯誤處理.py", line 3, in f1
    return 12/int(s)
ZeroDivisionError: integer division or modulo by zero
End

錯誤是一個class,例如python的內建函式:ValueError(),StandardError()…..或者我們自己定義一個錯誤類(必要的時候),但需要選擇好繼承關係例如:FooError(StandardError)…所以我們捕獲到的錯誤就是類的一個例項。
我們可以根據需要,選擇或定義一個錯誤類,用raise語句丟擲錯誤。

def f3(s):
    n=int(s)
    if n==0:
        raise StandardError('invalid value: %s' % s)
    return 12/0
f3('0')

執行如下:

Traceback (most recent call last):
  File "F:/python 程式碼/錯誤處理.py", line 21, in <module>
    f3('0')
  File "F:/python 程式碼/錯誤處理.py", line 19, in f3
    raise StandardError('invalid value: %s' % s)
StandardError: invalid value: 0

列印完錯誤資訊且程式繼續執行。

另一種非常常見的錯誤處理方式:

def foo1(s):
    n = int(s)
    return 10 / n
def bar1(s):
    try:
        return foo1(s) * 2
    except StandardError, e:
        print 'Error!'
        raise    

def main1():
    bar1('0')

main1()

執行結果如下:

Error!

Traceback (most recent call last):
  File "F:/python 程式碼/錯誤處理.py", line 30, in <module>
    main1()
  File "F:/python 程式碼/錯誤處理.py", line 28, in main1
    bar1('0')
  File "F:/python 程式碼/錯誤處理.py", line 22, in bar1
    return foo1(s) * 2
  File "F:/python 程式碼/錯誤處理.py", line 19, in foo1
    return 10 / n
ZeroDivisionError: integer division or modulo by zero

可見,編輯器已經知道了有錯誤,而且又把錯誤通過raise丟擲便於管理者儘快找出錯誤原因!若無raise則只輸出“Error”!

二、除錯
logging
與assert判斷或print列印錯誤相比,logging不會丟擲錯誤

import logging

s='0'
n=int(s)
logging.info('n=%d' %n)     #logging.info()可以輸出一段文字,且其需放在可能會出錯誤的語句之前
print 10/n

執行後只會輸出ZeroDivisionError,這跟logging制定記錄的錯誤資訊級別有關,有debug,info,warning,error等幾個級別,例如在import logging後匯入:

import logging
logging.basicConfig(level=logging.INFO)

會輸出

INFO:root:n = 0
Traceback (most recent call last):
  File "err.py", line 8, in <module>
    print 10 / n
ZeroDivisionError: integer division or modulo by zero

指定不同的級別就會輸出不同的資訊

======================================
學藝不精、未完待續