Python 簡明教程 --- 23,Python 異常處理

微信公衆號:碼農充電站pro
我的主頁:https://codeshellme.github.iohtml

要麼作第一個,要麼作最好的一個。java

目錄python

在這裏插入圖片描述

咱們在編寫程序時,總會不自覺的出現一些錯誤,好比邏輯錯誤語法錯誤和一些其它的運行時錯誤等。git

  • 邏輯錯誤: 這種錯誤不會致使程序崩潰,它不容易被發現,只有在執行結果不是咱們預期的時候,纔會被發現。
  • 語法錯誤: 這種錯誤是不符合語法規定的錯誤,說白了,就是編譯器或者解釋器沒法理解的代碼。出現這種錯誤時,程序是不能運行的。
  • 其它運行時錯誤: 這種錯誤是程序在運行的過程當中出現的,通常狀況下不會出現,可是極端狀況下會出現,是程序編寫者考慮不夠周全致使的。

在寫程序時必定要把全部的狀況都考慮到,而且處理掉,不能有僥倖心理(認爲某種狀況不會出現)。在程序中,只要是有可能出現的狀況,那就必定會出現。程序員

程序員也喜歡將這些錯誤戲稱爲bugbug表明軟件系統中的漏洞缺陷bug須要修正,不然程序是沒法正常運行的。重要的軟件系統若是出現漏洞,會帶來巨大的危害。所以,在軟件初步完成後,要進行嚴格的,全面的測試,不然將漏洞百出。github

Python 中提供了一套處理錯誤的機制,叫作異常shell

比較普通的處理錯誤的方法,是使用if 語句斷言assert來對各類狀況進行判斷,從而進行相應的處理。而異常是一種更加高級的處理錯誤的機制。微信

1,常見異常

咱們來看一些Python 中常見的異常。閉包

SyntaxError 異常函數

Python 語言有本身的語法格式和規則,若是咱們沒有遵照這些規則,將會出現異常:

>>> print('abc')- # 右括號後邊有一個橫線
  File "<stdin>", line 1
    print('abc')-
                ^
SyntaxError: invalid syntax

上尖括號^指出了異常出現的的位置。

NameError 異常

若是咱們使用了一個未定義的變量,將會出現該異常:

>>> print(a)  # a 變量未定義
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

ZeroDivisionError 異常

進行除法運算時,若是除數爲0,將會該異常:

>>> 1 / 0 # 除數爲 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

若是程序沒有處理異常,在異常出現時,將會崩潰退出,Python 解釋器會爲你定位到異常出現的位置,有助於快速的解決異常。

2,處理異常

異常須要捕獲,從而處理異常。若是在發生異常時,這個異常沒有被捕獲處理,這個異常將會一層一層的向上拋,直到這個異常被捕獲處理,或者程序崩潰。

try except 語句

在Python 中使用try 語句塊來捕獲異常,在except 語句塊中處理異常。

咱們通常將有可能出現異常的語句放在try 語句塊中,在except 語句塊中編寫處理異常的措施。

好比,咱們有以下代碼:

def hello(s):
    print('hello %s' % s)

hello('123', 'abc')

其中hello 函數的定義是須要接收一個參數,而在調用時,傳遞了兩個參數,運行此代碼將出現以下異常:

Traceback (most recent call last):
  File "Test.py", line 8, in <module>
    hello('123', 'abc')
TypeError: hello() takes 1 positional argument but 2 were given

代碼在遇到異常時,會在異常代碼處拋出異常,後邊的代碼將不會再執行。若是異常代碼沒有處理,程序將崩潰退出。

咱們能夠這樣處理該異常:

try:
    hello('123', 'abc')
except Exception as e:
    print(e)

咱們將調用語句放在了try 語句塊中,這樣就能夠捕獲異常。except 關鍵字的後邊是要捕獲的異常的名字as 後邊是捕獲到的異常 e。在except 語句塊中,咱們只是將捕獲到的異常打印了出來,運行該代碼,結果以下:

hello() takes 1 positional argument but 2 were given

可見錯誤被打印了出來。咱們還能夠在打印錯誤以後,再正確的調用hello 函數:

try:
    hello('123', 'abc')
except Exception as e:
    print(e)
    hello('123')

運行結果以下:

hello() takes 1 positional argument but 2 were given
hello 123

可見,運行到hello('123', 'abc') 時,出現異常,而後代碼執行到except 語句塊,print(e) 將錯誤打印出來,又執行了hello('123')

打印異常是最簡單的處理異常的方式,在工做中,咱們會將異常信息記錄在日誌文件中,這樣能夠將異常記錄下來,以便處理異常。

通常在捕獲異常時,儘可能只在try 語句塊中編寫有可能發生異常的代碼,基本不會發生異常的語句不要寫在try 塊中,這樣能夠減小try 塊中的代碼量,有助於定位問題。

except 語句

except 關鍵字後邊能夠跟一個異常名字,也能夠跟一組異常名字,一組異常時,將多個異常的名字寫在一個元組中,語法以下:

except (Error1, Error2...):
    pass

一個try 語句中,也能夠包含多個except 語句塊,語法以下:

try:
    # 代碼塊
except Error1 as e:
    # 處理 Error1
except Error2 as e:
    # 處理 Error2
    .
    .
    .
except:
    e = sys.exc_info()[0])
    pass

多個except 語句塊時,Python 解釋器會從上到下依次判斷異常的類型,直到符合某個異常時,會執行對應的語句塊中的代碼,在該except 塊以後的except 塊將被忽略。

其中,最後一個except 塊能夠省略異常的名字,這種格式能夠匹配任意的異常,在該塊中,可使用sys.exc_info()[0] 來獲取異常。

在有多個except 語句塊時,要注意,前邊的異常的範圍應該小於等於後邊的異常的範圍,不然,後邊的except 塊將沒有意義。

else 語句

Python 中,try... except... 以後還能夠有一個else語句塊,except 語句塊是在遇到異常時執行的,else 語句塊是在沒有遇到異常時執行的。

發生異常時,示例:

try:
    hello('123', 'abc')
except Exception as e:
    print('發生異常')
    print(e)
else:
    print('沒有發生異常')

上面的代碼中,執行到hello('123', 'abc') 時會發生異常,而後會進入到except 語句塊,else 語句不會被執行,執行結果以下:

發生異常
hello() takes 1 positional argument but 2 were given

沒有發生異常時,示例:

try:
    hello('123')
except Exception as e:
    print('發生異常')
    print(e)
else:
    print('沒有發生異常')

上面代碼中的try 語句塊不會發生異常,那麼except 語句就不會執行,else 語句會執行,結果以下:

hello 123
沒有發生異常

注意:

else 語句的使用頻率並不高。

finally 語句

finally 語句塊不管是否異常都會被執行,該語句塊常常用在須要關閉系統資源的狀況下。

沒有發生異常時,示例以下:

try:
    hello('123')
except Exception as e:
    print('發生異常')
    print(e)
else:
    print('沒有發生異常')
finally:
    print('執行了 finally 語句塊')

上面代碼中,try 語句塊沒有發生異常,elsefinally 都被執行,except 語句塊沒有執行。運行結果以下:

hello 123
沒有發生異常
執行了 finally 語句塊

發生異常時,示例以下:

try:
    hello('123', 'abc')
except Exception as e:
    print('發生異常')
    print(e)
else:
    print('沒有發生異常')
finally:
    print('執行了 finally 語句塊')

上面代碼中,try 語句塊發生異常,exceptfinally 都被執行,else 語句塊沒有執行。運行結果以下:

發生異常
hello() takes 1 positional argument but 2 were given
執行了 finally 語句塊

注意:

else 語句塊與finally 語句塊能夠同時存在,也能夠同時不存在,也能夠一個存在一個不存在,互不影響。

3,拋出異常

raise 異常

若是你捕獲了一個異常,卻不想完全解決這個異常,而想將該異常向上層拋出,可使用raise 關鍵字。

raise 用於拋出異常,其後能夠跟一個異常對象,或者什麼也不跟。

raise 後跟一個異常對象

raise Exception('這裏發生了錯誤')

raise 後什麼也不跟:

try:
    hello('123', 'abc')
except Exception as e:
    print('發生異常')
    raise

Python 解釋器會記錄最後一個發生的異常,raise 會將最後一個異常拋出。上面代碼中的raise 至關於raise e

assert 斷言

assert 語句稱爲斷言,就是判斷某個表達式是否爲真:

  • 表達式爲True 時,正常經過
  • 表達式爲False 時,拋出AssertionError 異常

示例以下:

表達式1 == 1True,沒有反應:

>>> assert 1 == 1

表達式1 == 0False,拋出異常:

>>> assert 1 == 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

4,Python 異常層次結構

Python 異常層次結構以下:

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

可參考這裏:

https://docs.python.org/3/library/exceptions.html#exception-hierarchy

這些都是內建異常BaseException 是全部異常的父類,咱們使用最多的是Exception 及其子類。可使用help(類名)來查看每一個類的詳情。

5,自定義異常

有時候,咱們須要定義本身的異常類,來知足本身的需求。

咱們已經知道,Python 異常類有本身的層次結構,全部的類都直接或者間接繼承了BaseException。所以,用戶自定義的異常類,也須要知足這種層次結構。

通常狀況下,自定義異常須要繼承Exception 類。以下:

class MyError(Exception):
    pass

MyError 類的使用方式,跟內建異常類的使用方式同樣。你能夠根據本身的須要,爲MyError 類編寫相應的構造方法,和其它類方法。

若是沒有爲MyError 編寫構造方法,那麼MyError 就繼承了Exception 的構造方法。

6,調試錯誤

程序編寫完成後不必定是正確的,當發現有錯誤時,就須要定位錯誤的位置。

最廣泛,最簡單的調錯的方法就是打印某個變量,經過輸出變量的值,來查看其是不是你想要的結果。

另外一種比較高效,有力的調試代碼的方式是單步調試,便是經過設置斷點,深刻到代碼內部,一步一步的跟蹤查看代碼的執行結果是否正確,從而達到修正代碼的目的。

C 語言中有一個很是著名的工具叫作gdb,這是一款強大的調試工具。Python 中也有相似的一款工具叫作pdb,它使用起來要比gdb 簡單許多。

在Python 中,pdb 是一個模塊,因此,在使用以前要先使用import pdb 將該模塊引入。而後,在須要調試代碼的地方,使用pdb.set_trace() 方法來設置斷點,在代碼執行到此處時,Python 解釋器就會今後處開始讓你調式代碼。

以下代碼,文件名爲Test.py

#! /usr/bin/env python3

# 引入 pdb 模塊
import pdb

def hello(s):
    print('hello %s' % s)

# 設置斷點
pdb.set_trace()

hello('python')
hello('java')

咱們使用python3 來運行該程序,以下:

$ python3 Test.py 
> ~/Test.py(12)<module>()
-> hello('python')
(Pdb)

能夠看到代碼在hello('python')以前暫停並進入斷點,控制檯顯示出(Pdb),咱們能夠在這個後面輸入Python 代碼或者pdb 支持的命令。

pdb 經常使用命令以下:

  • n:進行下一步代碼,即單步執行
  • c:代碼執行到下一個斷點處,若是沒有下一個斷點,則執行到程序結束
  • s:在遇到函數時,使用s 命令,能夠進入函數內部
  • l:列出當前語句周圍的10行代碼
  • p:用於輸出變量的值,至關於print 函數

(完。)

推薦閱讀:

Python 簡明教程 --- 18,Python 面向對象

Python 簡明教程 --- 19,Python 類與對象

Python 簡明教程 --- 20,Python 類中的屬性與方法

Python 簡明教程 --- 21,Python 繼承與多態

Python 簡明教程 --- 22,Python 閉包與裝飾器


歡迎關注做者公衆號,獲取更多技術乾貨。

碼農充電站pro

相關文章
相關標籤/搜索