1. 程式人生 > 實用技巧 >24. 錯誤、除錯和測試

24. 錯誤、除錯和測試

try:

try:
	print('try...')
	r = 10 / 0
	print('result:', r)
except ZeroDivisionError as e:
	print('except:', e)
finally:
	print('finally...')
print('END')
  • 從輸出可以看到,當錯誤發生時,後續語句print('result:', r)不會被執行,except由於捕獲到ZeroDivisionError,因此被執行。最後,finally語句被執行。然後,程式繼續按照流程往下走。
  • 由於沒有錯誤發生,所以except語句塊不會被執行,但是finally如果有,則一定會被執行(可以沒有finally語句)。
try:
	print('try...')
	r = 10 / int('2')
	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')
  • 如果沒有錯誤發生,可以在except語句塊後面加一個else,當沒有錯誤發生時,會自動執行else語句。

呼叫堆疊

​ 如果錯誤沒有被捕獲,它就會一直往上拋,最後被Python直譯器捕獲,列印一個錯誤資訊,然後程式退出。

捕獲錯誤的原因

​ 如果不捕獲錯誤,自然可以讓Python直譯器來打印出錯誤堆疊,但程式也被結束了。既然我們能捕獲錯誤,就可以把錯誤堆疊打印出來,然後分析錯誤原因,同時,讓程式繼續執行下去。

丟擲錯誤

​ 因為錯誤是class,捕獲一個錯誤就是捕獲到該class的一個例項。因此,錯誤並不是憑空產生的,而是有意建立並丟擲的。

# err_raise.py
class FooError(ValueError):
	pass
def foo(s):
	n = int(s)
	if n==0:
		raise FooError('invalid value: %s' % s)
	return 10 / n
foo('0')

斷言

凡是用print()來輔助檢視的地方,都可以用斷言(assert)來替代。

程式中如果到處充斥著assert,和print()相比也好不到哪去。不過,啟動Python直譯器時可以用-O引數來關閉assert;關閉後,你可以把所有的assert語句當成pass來看。

logging

把print()替換為logging是第3種方式,和assert比,logging不會丟擲錯誤,而且可以輸出到檔案。

import logging
# logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
232/531
print(10 / n)
  • logging.info()就可以輸出一段文字。執行,發現除了ZeroDivisionError,沒有任何資訊。

  • import logging之後新增一行配置後

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

    logging的好處,它允許指定記錄資訊的級別,有debug,info,warning,error等幾個級別