python中的異常處理

python中的異常

1、異常基礎

在python中,異常會根據錯誤自動地被觸發,也能由代碼觸發和截獲。 異常由四個語句進行處理:python

  • try/except: 捕捉由python或你引發的異常,並恢復。程序員

  • try/finally: 不管異常是否發生,執行清理行爲。不管是否異常,最後都必須執行finally;web

  • try/except/else/finally: 即先執行try,若是有異常,執行except;若是沒有異常,執行else;最後,都執行finally;數據庫

  • raise: 拋出異常,手動在代碼中觸發異常。函數

  • assert: 斷言,有條件地在程序員代碼中觸發異常。主要用於代碼調式 或 強制程序知足某個條件,不然報錯;spa

  • with/as: 在python2.6及後續版本中實現環境管理器。咱們在文件打開自動關閉那裏就用到了這個;調試

2、異常的角色

在python中,異常最多見的幾種角色:日誌

  • 錯誤處理
  • 事件通知
  • 特殊狀況處理
  • 終止行爲
  • 很是規控制流程

3、異常處理

默認異常處理器:打印標準出錯消息。這些消息包括引起的異常、堆棧跟蹤、用戶定義的異常等。code

假如try中,代碼塊有多個錯誤,可是,當遇到第一個錯誤時就停止,直接執行except;故try中的第二個錯誤就會捕捉不到;對象

4、舉例

一、異常處理

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語句拋出一個錯誤的實例:

案例1:自定義異常

  • 自定義異常,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)
    

案例2:raise向上級拋異常

  • 原版:

    # 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。雖然能夠轉換,可是必定不要轉換爲絕不相關的異常類型。

案例3:raise向Exception拋異常

  • 經過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。

斷言assert

其實,咱們能夠用斷言代替。凡是用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

logging

代碼調試時把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)

記住,日誌很重要!

pdb

此外,咱們還能夠利用啓動Python的調試器pdb,讓程序以單步方式運行,能夠隨時查看運行狀態。這裏再也不詳細講解這個啦。

相關文章
相關標籤/搜索