Python異常處理

區分Exception和Syntax Error

在寫Python程序的時候常常會報錯,報錯一般有如下兩種狀況:python

  1. 語法錯誤(Syntax Error): 部分語法錯誤屬於異常
  2. 異常(Exception)

語法錯誤

語法錯誤也稱爲解析錯誤,是最常遇到的一種錯誤程序員

In [1]: while True print('Hello!')
  File "<ipython-input-1-5c66e4fd0ae9>", line 1
    while True print('Hello!')
                   ^
SyntaxError: invalid syntax

當代碼不符合Python語法的時候就會拋出SyntaxError。網絡

異常

Python用異常對象來表示異常狀況。遇到錯誤後,會引起異常。若是異常沒有處理或捕捉,程序就會用traceback終止程序的執行,若是是在多線程程序中,則會終止當前線程的執行。多線程

In [2]: 1/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-2-05c9758a9c21> in <module>()
----> 1 1/0

ZeroDivisionError: division by zero

除以0時,就會拋出ZeroDivisionError異常(ZeroDivisionError類的一個實例)。異步

異常層次結構

Python 3.5.2中內置異常的類層次結構以下:參考標準庫ide

BaseException  # 全部異常的基類
 +-- SystemExit  # 程序退出/終止
 +-- KeyboardInterrupt  # 由鍵盤中斷(一般爲Ctrl+C) 生成
 +-- GeneratorExit  # 由生成器.close()方法引起
 +-- Exception  # 全部非退出異常的基類
      +-- StopIteration  # 中止迭代錯誤
      +-- StopAsyncIteration  # 中止異步迭代錯誤
      +-- ArithmeticError  # 算數異常的基類
      |    +-- FloatingPointError  # 浮點操做異常
      |    +-- OverflowError  # 溢出致使的異常
      |    +-- ZeroDivisionError  # 對0進行除或取模操做致使的異常
      +-- AssertionError  # 由assert語句引起
      +-- AttributeError  # 當屬性名稱無效時引起
      +-- BufferError  # 緩衝區錯誤引起
      +-- EOFError  # 到達文件結尾時引起
      +-- ImportError  # import語句失敗
      +-- LookupError  # 索引和鍵錯誤
      |    +-- IndexError  # 超出序列索引的範圍
      |    +-- KeyError  # 鍵不存在
      +-- MemoryError  # 內存不足
      +-- NameError  # 沒法找到局部或全局名稱
      |    +-- UnboundLocalError  # 未綁定的局部變量
      +-- OSError  # 操做系統錯誤
      |    +-- BlockingIOError  # IO阻塞
      |    +-- 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  # Unicode錯誤
      |         +-- UnicodeDecodeError  # Unicode解碼錯誤
      |         +-- UnicodeEncodeError  # Unicode編碼錯誤
      |         +-- UnicodeTranslateError  # Unicode轉換錯誤
      +-- Warning  # 警告的基類
           +-- DeprecationWarning  # 關於被棄用的特徵的警告
           +-- PendingDeprecationWarning  # 關於特性將會被廢棄的警告
           +-- RuntimeWarning  # 可疑的運行時行爲的警告
           +-- SyntaxWarning  # 可疑的語法的警告
           +-- UserWarning  # 用戶代碼生成的警告
           +-- FutureWarning  # 關於構造未來語義會有改變的警告
           +-- ImportWarning  # import語句的警告
           +-- UnicodeWarning  # Unicode警告
           +-- BytesWarning  # Bytes警告
           +-- ResourceWarning  # 資源警告
  • 全部異常的基類都是BaseException
  • 除SystemExit,KeyboardInterrupt,GeneratorExit三種異常外都繼承自Exception

捕獲異常

捕獲異常可使用try/except語句。try/except語句用來檢測try語句塊中的錯誤,從而讓except語句捕獲異常信息並處理。函數

try/except

基礎語法學習

try:
    <語句>
except <name>:
    <語句>          #若是在try部分引起了名爲'name'的異常,則執行這段代碼

示例ui

In [3]: try:
   ...:     x = int(input("Please enter a number: "))
   ...: except ValueError:
   ...:     print("No valid number.")
   ...:     
Please enter a number: asd
No valid number.

多個except

In [4]: import sys

In [5]: try:
   ...:     f = open('file.txt')  # 文件不存在的時候就會拋出FileNotFoundError異常
   ...:     s = f.readline()
   ...:     i = int(s.strip())
   ...: except OSError:  # FileNotFoundError異常的上層異常
   ...:     print('OS error.')
   ...: except ValueError:
   ...:     print('Could not convert data to integer.')
   ...: except Exception:
   ...:     print('Exception.')
   ...: except:  # 不加具體異常類型時,會捕獲全部的異常,應該不用或者慎用
   ...:     print('Unexpected error:', sys.exc_info()[0])
   ...:     
OS error.

各個except之間的執行順序:編碼

  • except順序捕獲try中拋出的異常
  • 越具體的異常應該越靠前,越通常的異常應該越靠後

可選的else語句

語法

try:
    <語句>
except <name>:
    <語句>          #若是在try部分引起了名爲'name'的異常,則執行這段代碼
else:
    <語句>          #若是沒有異常發生,則執行這段代碼

若是try部分沒有拋出異常,可是又必須執行的語句,則放在else語句中。

代碼

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:  # 沒有拋出異常(即文件正確打開)時打印出文件中的每一行
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

finally語句

finally語句用來定義在任何狀況下都必須執行的語句。

In [1]: try:
   ...:     raise KeyboardInterrupt
   ...: finally:
   ...:     print('Goodbye')
   ...:     
Goodbye
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-8-132d568ca0fb> in <module>()
      1 try:
----> 2     raise KeyboardInterrupt
      3 finally:
      4     print('Goodbye')
      5 

KeyboardInterrupt:

帶return語句的finally執行順序

def p(x):
    print(x)
    return x

def t():
    try:
        return p(2)
        print('haha')
    finally:
        return p(3)

x = t()

# 輸出結果爲:
2
3
# 返回值x爲3

可見,在try塊中,只要有finally語句,即便函數提早返回,也會在退出try塊以前執行finally語句,所以返回值會被finally中的return語句替代。

綜合使用示例

In [1]: def divide(x, y):
   ...:     try:
   ...:         result = x / y
   ...:     except ZeroDivisionError:
   ...:         print('division by zero!')
   ...:     else:
   ...:         print('result is ', result)
   ...:     finally:
   ...:         print('executing finally clause.')
   ...:         

In [2]: divide(2, 0)
division by zero!
executing finally clause.

In [3]: divide(2, 1)
result is  2.0
executing finally clause.

In [4]: divide('2', '1')
executing finally clause.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-34bb38fa74fd> in <module>()
----> 1 divide('2', '1')

<ipython-input-1-4273ffa41b76> in divide(x, y)
      1 def divide(x, y):
      2     try:
----> 3         result = x / y
      4     except ZeroDivisionError:
      5         print('division by zero!')

TypeError: unsupported operand type(s) for /: 'str' and 'str'

結論:

  • 任何狀況下finally語句都會執行。
  • 即便try部分中有return語句,也會在退出try塊以前執行finally語句,而且返回值是finally中的return
  • 若是有異常沒有被處理,則在執行完成finally語句以後會會拋出沒有被處理的異常
  • 在實際使用中,finally一般用來釋放額外的資源,好比文件或者網絡鏈接

主動拋出異常

raise語句

In [1]: raise NameError('Hello')
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-64f372e60821> in <module>()
----> 1 raise NameError('Hello')

NameError: Hello

用戶自定義異常

用戶自定義異常類時,應該直接或者間接的繼承自Exception類。

class CustomException(Exception):
    def __init__(self, code, message):
        self.code = code
        self.message = message

try:
    raise CustomException(500, 'error')
except CustomException as e:
    print('{},{}'.format(e.code, e.message))

# 輸出結果:500,error

異常的傳遞

在函數內引起異常時,若是異常沒有被捕獲到,那麼它就會被傳播到函數被調用的地方。

In [1]: def a():
   ...:         raise Exception('Hello')
   ...: 

In [2]: def b():
   ...:         print('enter b')
   ...:         a()  # 函數a中引起的異常,會傳遞到父函數的調用出
   ...:         print('exit b')  # a中拋出異常以後傳遞到b,停止b的執行
   ...:     

In [3]: b()
enter b
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-3-9c619ddbd09b> in <module>()
----> 1 b()

<ipython-input-2-f99a565bd6f8> in b()
      1 def b():
      2         print('enter b')
----> 3         a()
      4         print('exit b')
      5 

<ipython-input-1-6e68e60e93b5> in a()
      1 def a():
----> 2         raise Exception('Hello')

Exception: Hello

記得幫我點贊哦!

精心整理了計算機各個方向的從入門、進階、實戰的視頻課程和電子書,按照目錄合理分類,總能找到你須要的學習資料,還在等什麼?快去關注下載吧!!!

resource-introduce

念念不忘,必有迴響,小夥伴們幫我點個贊吧,很是感謝。

我是職場亮哥,YY高級軟件工程師、四年工做經驗,拒絕鹹魚爭當龍頭的斜槓程序員。

聽我說,進步多,程序人生一把梭

若是有幸能幫到你,請幫我點個【贊】,給個關注,若是能順帶評論給個鼓勵,將不勝感激。

職場亮哥文章列表:更多文章

wechat-platform-guide-attention

本人全部文章、回答都與版權保護平臺有合做,著做權歸職場亮哥全部,未經受權,轉載必究!

相關文章
相關標籤/搜索