Python學習目錄python
在程序運行過程當中,總會遇到各類各樣的錯誤,Python內置了一套異常處理機制,來幫助咱們進行錯誤處理。編程
在程序運行的過程當中,若是發生了錯誤,能夠事先約定返回一個錯誤代碼,這樣,就能夠知道是否有錯,以及出錯的緣由。在操做系統提供的調用中,返回錯誤碼很是常見。好比打開文件的函數open()
,成功時返回文件描述符(就是一個整數),出錯時返回-1
。網絡
用錯誤碼來表示是否出錯十分不便,由於函數自己應該返回的正常結果和錯誤碼混在一塊兒,形成調用者必須用大量的代碼來判斷是否出錯,因此高級語言一般都內置了一套try...except...finally...
的錯誤處理機制,Python也不例外。eclipse
try:
print('try...')
r = 10 / 0
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally...')
print('END')
複製代碼
當咱們認爲某些代碼可能會出錯時,就能夠用try
來運行這段代碼,若是執行出錯,則後續代碼不會繼續執行,而是直接跳轉至錯誤處理代碼,即except
語句塊,執行完except
後,若是有finally
語句塊,則執行finally
語句塊,至此,執行完畢。函數式編程
Python的錯誤其實也是class,全部的錯誤類型都繼承自BaseException
,因此在使用except
時須要注意的是,它不但捕獲該類型的錯誤,還把其子類也「一網打盡」。函數
若是錯誤沒有被捕獲,它就會一直往上拋,最後被Python解釋器捕獲,打印一個錯誤信息,而後程序退出。來看看err.py
:工具
# err.py:
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
bar('0')
main()
複製代碼
執行,結果以下:post
$ python3 err.py
Traceback (most recent call last):
File "err.py", line 11, in <module>
main()
File "err.py", line 9, in main
bar('0')
File "err.py", line 6, in bar
return foo(s) * 2
File "err.py", line 3, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
複製代碼
咱們從上往下能夠看到整個錯誤的調用函數鏈。單元測試
若是不捕獲錯誤,天然可讓Python解釋器來打印出錯誤堆棧,但程序也被結束了。既然咱們能捕獲錯誤,就能夠把錯誤堆棧打印出來,而後分析錯誤緣由,同時,讓程序繼續執行下去。學習
Python內置的logging
模塊能夠很是容易地記錄錯誤信息:
# err_logging.py
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()
print('END')
複製代碼
執行上述模塊:
$ python3 err_logging.py
ERROR:root:division by zero
Traceback (most recent call last):
File "err_logging.py", line 13, in main
bar('0')
File "err_logging.py", line 9, in bar
return foo(s) * 2
File "err_logging.py", line 6, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
END
複製代碼
由於錯誤是class,捕獲一個錯誤就是捕獲到該class的一個實例。所以,錯誤並非憑空產生的,而是有意建立並拋出的。Python的內置函數會拋出不少類型的錯誤,咱們本身編寫的函數也能夠拋出錯誤。
若是要拋出錯誤,首先根據須要,能夠定義一個錯誤的class,選擇好繼承關係,而後,用raise
語句拋出一個錯誤的實例:
# 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')
複製代碼
執行,能夠最後跟蹤到咱們本身定義的錯誤:
$ python3 err_raise.py
Traceback (most recent call last):
File "err_throw.py", line 11, in <module>
foo('0')
File "err_throw.py", line 8, in foo
raise FooError('invalid value: %s' % s)
__main__.FooError: invalid value: 0
複製代碼
最後,咱們來看另外一種錯誤處理的方式:
# err_reraise.py
def foo(s):
n = int(s)
if n==0:
raise ValueError('invalid value: %s' % s)
return 10 / n
def bar():
try:
foo('0')
except ValueError as e:
print('ValueError!')
raise
bar()
複製代碼
在bar()
函數中,咱們明明已經捕獲了錯誤,可是,打印一個ValueError!
後,又把錯誤經過raise
語句拋出去了,這不有病麼?
其實這種錯誤處理方式不但沒病,並且至關常見。捕獲錯誤目的只是記錄一下,便於後續追蹤。可是,因爲當前函數不知道應該怎麼處理該錯誤,因此,最恰當的方式是繼續往上拋,讓頂層調用者去處理。比如一個員工處理不了一個問題時,就把問題拋給他的老闆,若是他的老闆也處理不了,就一直往上拋,最終會拋給CEO去處理。
raise
語句若是不帶參數,就會把當前錯誤原樣拋出。此外,在except
中raise
一個Error,還能夠把一種類型的錯誤轉化成另外一種類型:
try:
10 / 0
except ZeroDivisionError:
raise ValueError('input error!')
複製代碼
程序能一次寫完並正常運行的機率很小,基本不超過1%。總會有各類各樣的bug須要修正。有的bug很簡單,看看錯誤信息就知道,有的bug很複雜,咱們須要知道出錯時,哪些變量的值是正確的,哪些變量的值是錯誤的,所以,須要一整套調試程序的手段來修復bug。
用print()
把可能有問題的變量打印出來看看。
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
複製代碼
assert
的意思是,表達式n != 0
應該是True
,不然,根據程序運行的邏輯,後面的代碼確定會出錯。
若是斷言失敗,assert
語句自己就會拋出AssertionError
。
logging容許你指定記錄信息的級別,有debug
,info
,warning
,error
等幾個級別,當咱們指定level=INFO
時,logging.debug
就不起做用了。同理,指定level=WARNING
後,debug
和info
就不起做用了。這樣一來,你能夠放心地輸出不一樣級別的信息,也不用刪除,最後統一控制輸出哪一個級別的信息。
logging
的另外一個好處是經過簡單的配置,一條語句能夠同時輸出到不一樣的地方,好比console和文件。
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
複製代碼
Python的調試器pdb,讓程序以單步方式運行,能夠隨時查看運行狀態。咱們先準備好程序:
# err.py
s = '0'
n = int(s)
print(10 / n)
複製代碼
而後啓動:
$ python -m pdb err.py
> /Users/michael/Github/learn-python3/samples/debug/err.py(2)<module>()
-> s = '0'
複製代碼
以參數-m pdb
啓動後,pdb定位到下一步要執行的代碼-> s = '0'
。輸入命令l
來查看代碼:
(Pdb) l
1 # err.py
2 -> s = '0'
3 n = int(s)
4 print(10 / n)
複製代碼
輸入命令n
能夠單步執行代碼。
這個方法也是用pdb,可是不須要單步執行,咱們只須要import pdb
,而後,在可能出錯的地方放一個pdb.set_trace()
,就能夠設置一個斷點:
# err.py
import pdb
s = '0'
n = int(s)
pdb.set_trace() # 運行到這裏會自動暫停
print(10 / n)
複製代碼
運行代碼,程序會自動在pdb.set_trace()
暫停並進入pdb調試環境,能夠用命令p
查看變量,或者用命令c
繼續運行。
目前比較好的Python IDE有:
isual Studio Code:code.visualstudio.com/,須要安裝Python插件。
PyCharm:www.jetbrains.com/pycharm/
另外,Eclipse加上pydev插件也能夠調試Python程序。
單元測試是用來對一個模塊、一個函數或者一個類來進行正確性檢驗的測試工做。
若是你常常閱讀Python的官方文檔,能夠看到不少文檔都有示例代碼。
能夠把這些示例代碼在Python的交互式環境下輸入並執行,結果與文檔中的示例代碼顯示的一致。
這些代碼與其餘說明能夠寫在註釋中,而後,由一些工具來自動生成文檔。