在python中,異常會根據錯誤自動地被觸發,也能由代碼觸發和截獲。 異常由四個語句進行處理:python
try/except: 捕捉由python或你引發的異常,並恢復。程序員
try/finally: 不管異常是否發生,執行清理行爲。不管是否異常,最後都必須執行finally;web
try/except/else/finally: 即先執行try,若是有異常,執行except;若是沒有異常,執行else;最後,都執行finally;數據庫
raise: 拋出異常,手動在代碼中觸發異常。函數
assert: 斷言,有條件地在程序員代碼中觸發異常。主要用於代碼調式 或 強制程序知足某個條件,不然報錯;spa
with/as: 在python2.6及後續版本中實現環境管理器。咱們在文件打開自動關閉那裏就用到了這個;調試
在python中,異常最多見的幾種角色:日誌
默認異常處理器:打印標準出錯消息。這些消息包括引起的異常、堆棧跟蹤、用戶定義的異常等。code
假如try中,代碼塊有多個錯誤,可是,當遇到第一個錯誤時就停止,直接執行except;故try中的第二個錯誤就會捕捉不到;對象
try: print('try...') r = 10 / 0 print('result:', r) except ZeroDivisionError as e: # python3中,須要用as print('except:', e) finally: print ('finally...') print ('END')
用try來運行這段代碼,若是執行出錯,則後續代碼不會繼續執行,而是直接跳轉至錯誤處理代碼,即except語句塊,執行完except後,若是有finally語句塊,則執行finally語句塊,至此,執行完畢。
若是有多個except,前面匹配到了錯誤,則後面的except不會捕獲錯誤。例如:
try: print('try...') r = 10 / 0 print('result:', r) except BaseException as e: print('except1:',e) except ZeroDivisionError as e: print('except2:', e) finally: print ('finally...') print ('END')
從上面例子中,能夠發現不管什麼錯誤,都是except1捕獲。由於Python的錯誤其實也是class,全部的錯誤類型都繼承自BaseException,任何錯誤都是BaseException的子集;
python3.6中異常關係以下:
BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- StopAsyncIteration +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError | +-- ZeroDivisionError +-- AssertionError +-- AttributeError +-- BufferError +-- EOFError +-- ImportError | +-- ModuleNotFoundError +-- LookupError | +-- IndexError | +-- KeyError +-- MemoryError +-- NameError | +-- UnboundLocalError +-- OSError | +-- BlockingIOError | +-- ChildProcessError | +-- ConnectionError | | +-- BrokenPipeError | | +-- ConnectionAbortedError | | +-- ConnectionRefusedError | | +-- ConnectionResetError | +-- FileExistsError | +-- FileNotFoundError | +-- InterruptedError | +-- IsADirectoryError | +-- NotADirectoryError | +-- PermissionError | +-- ProcessLookupError | +-- TimeoutError +-- ReferenceError +-- RuntimeError | +-- NotImplementedError | +-- RecursionError +-- SyntaxError | +-- IndentationError | +-- TabError +-- SystemError +-- TypeError +-- ValueError | +-- UnicodeError | +-- UnicodeDecodeError | +-- UnicodeEncodeError | +-- UnicodeTranslateError +-- Warning +-- DeprecationWarning +-- PendingDeprecationWarning +-- RuntimeWarning +-- SyntaxWarning +-- UserWarning +-- FutureWarning +-- ImportWarning +-- UnicodeWarning +-- BytesWarning +-- ResourceWarning
由於錯誤是class,捕獲一個錯誤就是捕獲到該class的一個實例。所以,錯誤並非憑空產生的,而是有意建立並拋出的。Python的內置函數會拋出不少類型的錯誤,咱們本身編寫的函數也能夠拋出錯誤。
若是要拋出錯誤,首先根據須要,能夠定義一個錯誤的class,選擇好繼承關係,而後,用raise語句拋出一個錯誤的實例:
自定義異常,print一個對象,實際是兩個步驟:1.執行該對象的__str__方法;2.print獲得該對象的__str__的返回值,打印
class FooError(Exception): def __init__(self,msg): self.message = msg def __str__(self): return self.message try: raise FooError('自定義異常FooError.') except FooError as e: print(e)
原版:
# err.py class FooError(Exception): #定義錯誤的class,並指定父類; pass def foo(s): n = int(s) if n==0: raise FooError('invalid value: %s' % s) return 10 / n
這時,執行該腳本,會報出異常:
Traceback (most recent call last): File "/root/test02.py", line 22, in foo(0) File "/root/test02.py", line 19, in foo raise FooError('invalid value: %s' % s) main.FooError: invalid value: 0
通常咱們都儘可能使用python自帶的異常類型便可。
改進版:
def foo(s): n = int(s) return 10 / n def bar(s): try: return foo(s) except BaseException as e: print ('Error!',e) raise #僅僅捕獲錯誤,不知道應該怎麼處理該錯誤,因此,最恰當的方式是利用raise繼續往上拋,讓頂層調用者去處理。 def main(): bar('0') main()
raise語句若是不帶參數,就會把當前錯誤原樣拋出。此外,在except中raise一個Error,還能夠把一種類型的錯誤轉化成另外一種類型:
try: 10 / 0 except ZeroDivisionError: raise ValueError('input error!')
這裏將ZeroDivisionError 異常類型轉換爲了 ValueError。雖然能夠轉換,可是必定不要轉換爲絕不相關的異常類型。
經過raise,拋出指定異常給Exception,能夠和Exception一塊兒記錄到日誌中;
def db(): try: pass # 鏈接數據庫,操做 except Exception as e: return False else: return True def log(n): print(n) pass # 打開文件,記錄日誌 def make(): try: result = db() if not result: raise Exception("數據庫操做失敗.") # 這裏是將錯誤信息拋給Exception,這樣能夠經過下面log()記錄到日誌文件中。 except Exception as e: str_error = str(e) log(str_error) make()
咱們寫代碼,都不可能一次搞定。調試時若是使用print語句,那麼調試完畢後,代碼中處處都是print,咱們還得手動刪除這些print。
其實,咱們能夠用斷言代替。凡是用print來輔助查看的地方,均可以用斷言(assert)來替代:
def foo(s): n = int(s) assert n != 0, 'n is zero!!!' return 10 / n def main(): foo('0') main()
assert的意思是,表達式n != 0應該是True,不然,後面的代碼就會出錯。 若是斷言失敗,assert語句自己就會拋出AssertionError:
Traceback (most recent call last): ...... assert n != 0, 'n is zero!!!' AssertionError: n is zero!!! #注意,這裏的異常信息是咱們斷言處自定義的。
程序中若是處處充斥着assert,和print相比也好不到哪去。不過,啓動Python解釋器時能夠用-O參數來關閉assert(把assert當作pass處理):
# python -O test02.py
代碼調試時把print替換爲logging,和assert比,logging不會拋出錯誤,並且能夠輸出到文件:
# err.py import logging logging.basicConfig(level=logging.DEBUG) s = '2' n = int(s) logging.info('n = %d' % n) print (10 / n)
記住,日誌很重要!
此外,咱們還能夠利用啓動Python的調試器pdb,讓程序以單步方式運行,能夠隨時查看運行狀態。這裏再也不詳細講解這個啦。