Python 之 錯誤,除錯和測試

ckxllf發表於2020-03-03

  錯誤處理

  在預防程式發生執行錯誤的時候,可以預先設定返回一個錯誤程式碼,然後在執行程式的時候,就會知道程式是否正確。

  用錯誤碼來表示是否出錯有時候也十分的不方便,因為函式本身應該返回的正常結果和錯誤碼混在一起,會導致必須用大量的程式碼去判斷程式碼是否出錯:

  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

  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用來處理錯誤十分方便。出錯時,會分析錯誤資訊並定位錯誤發生的程式碼位置才是最關鍵的。

  程式也可以主動丟擲錯誤,讓呼叫者來處理相應的錯誤。但是,應該在文件中寫清楚可能會丟擲哪些錯誤,以及錯誤產生的原因。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69945560/viewspace-2678245/,如需轉載,請註明出處,否則將追究法律責任。

相關文章