Python隨手筆記(十)--------錯誤,調試和測試(1)

錯誤處理

在預防程序發生運行錯誤的時候,可以預先設置返回一個錯誤代碼,然後在執行程序的時候,就會知道程序是否正確。
用錯誤碼來表示是否出錯有時候也十分的不方便,因爲函數本身應該返回的正常結果和錯誤碼混在一起,會導致必須用大量的代碼去判斷代碼是否出錯:

def foo():
    r = some_function()
    if r == (-1):
        return -1
    return r
def bar():
    r = foo()
    if r==(-1):
        print('Error')
    else:
        pass

一旦發生錯誤,還需要各個函數之間互相傳遞,用起來不方便,所以try…except…finally就被廣泛的用於試驗錯誤。

try

下面這個例子可以讓我們瞭解try的使用方法:

>>> try:
	print("try...")
	r = 10 / 0
	print("result:",r)
except ZeroDivisionError as e:
	print("except:",e)
finally:
	print("Finally")

	
try...
except: division by zero
Finally

如果執行出錯,則後續代碼不會繼續執行,而是直接跳轉至錯誤處理代碼,即except語句塊,執行完except後,如果有finally語句塊,則執行finally語句塊,至此,執行完畢。
如果將上面的0改成5,則不會報錯,正常運行。

>>> try:
	print("try...")
	r = 10 / 5
	print("result:",r)
except ZeroDivisionError as e:
	print("except:",e)
finally:
	print("Finally")

	
try...
result: 2.0
Finally

可以看出不再執行except語句,只要一個程序中有finally語句,他肯定會被執行。

int()函數可能會拋出ValueError,所以我們用一個except捕獲ValueError,用另一個except捕獲ZeroDivisionError。此外,如果沒有錯誤發生,可以在except語句塊後面加一個else,當沒有錯誤發生時,會自動執行else語句:

try:
    print('try...')
    r = 10 / int('5')
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
else:
    print('no error!')
finally:
    print('finally...')
print('END')

Python的錯誤其實也是一種類,所有的錯誤類型都繼承自BaseException,所以在使用except不但捕獲該類型的錯誤,還把其子類也“一網打盡”:

try:
    foo()
except ValueError as e:
    print('ValueError')
except UnicodeError as e:
    print('UnicodeError')

第二個except永遠也捕獲不到UnicodeError,因爲UnicodeError是ValueError的子類,如果有,也被第一個except給捕獲了。
常見錯誤及其關係

不需要在每個可能出錯的地方去捕獲錯誤,只要在合適的層次去捕獲錯誤就可以了。

def foo(s):
    return 10 / int(s)


def bar(s):
    return foo(s) * 2


def main():
    try:
        bar('0')
    except Exception as e:
        print('Error:', e)
    finally:
        print('finally...')

調用棧

如果錯誤沒有被捕獲,它就會一直往上拋,最後被Python解釋器捕獲,打印一個錯誤信息,然後程序退出。下面這個例子可以很好地展示這種操作:

def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main():
    bar('0')

main()

運行顯示結果:

Traceback (most recent call last):
  File "F:/python文件/tiaoshi/__init__.py", line 10, in <module>
    main()
  File "F:/python文件/tiaoshi/__init__.py", line 8, in main
    bar('0')
  File "F:/python文件/tiaoshi/__init__.py", line 5, in bar
    return foo(s) * 2
  File "F:/python文件/tiaoshi/__init__.py", line 2, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero

下面我們就來解讀一下這個輸出的結果,首先第一句:

Traceback (most recent call last):

告訴我們這是錯誤的跟蹤信息。

File “F:/python文件/tiaoshi/init.py”, line 10, in <\module>
main()

代碼中第10行調用main函數出錯,原因是第8行.

File “F:/python文件/tiaoshi/init.py”, line 8, in main
bar(‘0’)

代碼中第8行調用bar函數出錯,原因是第5行.

File “F:/python文件/tiaoshi/init.py”, line 5, in bar
return foo(s) * 2

代碼中第5行返回foo函數出錯,原因是return 10 / int(s)這個語句出錯了,這是錯誤產生的源頭,因爲下面打印了:

ZeroDivisionError: integer division or modulo by zero

根據錯誤類型ZeroDivisionError,我們判斷,int(s)本身並沒有出錯,但是int(s)返回0,在計算10 / 0時出錯,至此,找到錯誤源頭。
標題出錯的時候,一定要分析錯誤的調用棧信息,才能定位錯誤的位置。

記錄錯誤

如果不捕獲錯誤,自然可以讓Python解釋器來打印出錯誤堆棧,但程序也被結束了。既然我們能捕獲錯誤,就可以把錯誤堆棧打印出來,然後分析錯誤原因,同時,讓程序繼續執行下去。Python內置的logging模塊可以非常容易地記錄錯誤信息:

import logging


def foo(s):
    return 10 / int(s)


def bar(s):
    return foo(s) * 2


def main():
    try:
        bar('0')
    except Exception as e:
        logging.exception(e)


main()

輸出結果:

END
ERROR:root:division by zero
Traceback (most recent call last):
  File "F:/python文件/tiaoshi/__init__.py", line 14, in main
    bar('0')
  File "F:/python文件/tiaoshi/__init__.py", line 9, in bar
    return foo(s) * 2
  File "F:/python文件/tiaoshi/__init__.py", line 5, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero

Python內置的try…except…finally用來處理錯誤十分方便。出錯時,會分析錯誤信息並定位錯誤發生的代碼位置纔是最關鍵的。

程序也可以主動拋出錯誤,讓調用者來處理相應的錯誤。但是,應該在文檔中寫清楚可能會拋出哪些錯誤,以及錯誤產生的原因。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章