微信公衆號:碼農充電站pro
我的主頁:https://codeshellme.github.iohtml
要麼作第一個,要麼作最好的一個。java
目錄python
咱們在編寫程序時,總會不自覺的出現一些錯誤,好比邏輯錯誤
,語法錯誤
和一些其它的運行時錯誤
等。git
崩潰
,它不容易被發現,只有在執行結果不是咱們預期的時候,纔會被發現。編譯器
或者解釋器
沒法理解的代碼。出現這種錯誤時,程序是不能運行的。在寫程序時必定要把全部的狀況都考慮到,而且處理掉,不能有僥倖心理(認爲某種狀況不會出現)。在程序中,只要是有可能
出現的狀況,那就必定
會出現。程序員
程序員也喜歡將這些錯誤戲稱爲bug
,bug
表明軟件系統中的漏洞
或缺陷
,bug
須要修正,不然程序是沒法正常運行的。重要的軟件系統若是出現漏洞,會帶來巨大的危害。所以,在軟件初步完成後,要進行嚴格的,全面的測試,不然將漏洞百出。github
Python 中提供了一套處理錯誤的機制,叫作異常
。shell
比較普通的處理錯誤的方法,是使用if 語句
或斷言assert
來對各類狀況進行判斷,從而進行相應的處理。而異常
是一種更加高級的處理錯誤的機制。微信
咱們來看一些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 解釋器會爲你定位到異常
出現的位置,有助於快速的解決異常。
異常須要捕獲,從而處理異常。若是在發生異常時,這個異常沒有被捕獲處理,這個異常將會一層一層的向上拋,直到這個異常被捕獲處理,或者程序崩潰。
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
語句塊沒有發生異常,else
與 finally
都被執行,except
語句塊沒有執行。運行結果以下:
hello 123 沒有發生異常 執行了 finally 語句塊
發生異常時,示例以下:
try: hello('123', 'abc') except Exception as e: print('發生異常') print(e) else: print('沒有發生異常') finally: print('執行了 finally 語句塊')
上面代碼中,try
語句塊發生異常,except
與 finally
都被執行,else
語句塊沒有執行。運行結果以下:
發生異常 hello() takes 1 positional argument but 2 were given 執行了 finally 語句塊
注意:
else
語句塊與finally
語句塊能夠同時存在,也能夠同時不存在,也能夠一個存在一個不存在,互不影響。
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 == 1
爲True
,沒有反應:
>>> assert 1 == 1
表達式1 == 0
爲False
,拋出異常:
>>> assert 1 == 0 Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError
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(類名)
來查看每一個類的詳情。
有時候,咱們須要定義本身的異常類
,來知足本身的需求。
咱們已經知道,Python 異常類有本身的層次結構,全部的類都直接或者間接繼承了BaseException
。所以,用戶自定義的異常類,也須要知足這種層次結構。
通常狀況下,自定義異常須要繼承Exception
類。以下:
class MyError(Exception): pass
MyError
類的使用方式,跟內建異常類的使用方式同樣。你能夠根據本身的須要,爲MyError
類編寫相應的構造方法,和其它類方法。
若是沒有爲MyError
編寫構造方法,那麼MyError
就繼承了Exception
的構造方法。
程序編寫完成後不必定是正確的,當發現有錯誤時,就須要定位錯誤的位置。
最廣泛,最簡單的調錯的方法就是打印某個變量,經過輸出變量的值,來查看其是不是你想要的結果。
另外一種比較高效,有力的調試代碼的方式是單步調試
,便是經過設置斷點
,深刻到代碼內部,一步一步的跟蹤查看代碼的執行結果是否正確,從而達到修正代碼的目的。
在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 閉包與裝飾器
歡迎關注做者公衆號,獲取更多技術乾貨。