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

要么做第一個(gè),要么做最好的一個(gè)统锤。

目錄

我們?cè)诰帉懗绦驎r(shí)毛俏,總會(huì)不自覺的出現(xiàn)一些錯(cuò)誤,比如邏輯錯(cuò)誤饲窿,語法錯(cuò)誤和一些其它的運(yùn)行時(shí)錯(cuò)誤等煌寇。

  • 邏輯錯(cuò)誤: 這種錯(cuò)誤不會(huì)導(dǎo)致程序崩潰,它不容易被發(fā)現(xiàn)逾雄,只有在執(zhí)行結(jié)果不是我們預(yù)期的時(shí)候阀溶,才會(huì)被發(fā)現(xiàn)。
  • 語法錯(cuò)誤: 這種錯(cuò)誤是不符合語法規(guī)定的錯(cuò)誤鸦泳,說白了银锻,就是編譯器或者解釋器無法理解的代碼。出現(xiàn)這種錯(cuò)誤時(shí)做鹰,程序是不能運(yùn)行的击纬。
  • 其它運(yùn)行時(shí)錯(cuò)誤: 這種錯(cuò)誤是程序在運(yùn)行的過程中出現(xiàn)的,一般情況下不會(huì)出現(xiàn)钾麸,但是極端情況下會(huì)出現(xiàn)更振,是程序編寫者考慮不夠周全導(dǎo)致的炕桨。

在寫程序時(shí)一定要把所有的情況都考慮到,并且處理掉肯腕,不能有僥幸心理(認(rèn)為某種情況不會(huì)出現(xiàn))献宫。在程序中,只要是有可能出現(xiàn)的情況实撒,那就一定會(huì)出現(xiàn)遵蚜。

程序員也喜歡將這些錯(cuò)誤戲稱為bugbug代表軟件系統(tǒng)中的漏洞缺陷奈惑,bug需要修正吭净,否則程序是無法正常運(yùn)行的。重要的軟件系統(tǒng)如果出現(xiàn)漏洞肴甸,會(huì)帶來巨大的危害寂殉。因此,在軟件初步完成后原在,要進(jìn)行嚴(yán)格的友扰,全面的測試,否則將漏洞百出庶柿。

Python 中提供了一套處理錯(cuò)誤的機(jī)制村怪,叫做異常

比較普通的處理錯(cuò)誤的方法浮庐,是使用if 語句斷言assert來對(duì)各種情況進(jìn)行判斷甚负,從而進(jìn)行相應(yīng)的處理。而異常是一種更加高級(jí)的處理錯(cuò)誤的機(jī)制审残。

1梭域,常見異常

我們來看一些Python 中常見的異常。

SyntaxError 異常

Python 語言有自己的語法格式和規(guī)則搅轿,如果我們沒有遵守這些規(guī)則病涨,將會(huì)出現(xiàn)異常:

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

上尖括號(hào)^指出了異常出現(xiàn)的的位置。

NameError 異常

如果我們使用了一個(gè)未定義的變量璧坟,將會(huì)出現(xiàn)該異常:

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

ZeroDivisionError 異常

進(jìn)行除法運(yùn)算時(shí)既穆,如果除數(shù)為0,將會(huì)該異常:

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

如果程序沒有處理異常雀鹃,在異常出現(xiàn)時(shí)幻工,將會(huì)崩潰退出,Python 解釋器會(huì)為你定位到異常出現(xiàn)的位置褐澎,有助于快速的解決異常会钝。

2,處理異常

異常需要捕獲工三,從而處理異常迁酸。如果在發(fā)生異常時(shí),這個(gè)異常沒有被捕獲處理俭正,這個(gè)異常將會(huì)一層一層的向上拋奸鬓,直到這個(gè)異常被捕獲處理,或者程序崩潰掸读。

try except 語句

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

我們一般將有可能出現(xiàn)異常的語句放在try 語句塊中儿惫,在except 語句塊中編寫處理異常的措施澡罚。

比如,我們有如下代碼:

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

hello('123', 'abc')

其中hello 函數(shù)的定義是需要接收一個(gè)參數(shù)肾请,而在調(diào)用時(shí)留搔,傳遞了兩個(gè)參數(shù),運(yùn)行此代碼將出現(xiàn)如下異常:

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

代碼在遇到異常時(shí)铛铁,會(huì)在異常代碼處拋出異常隔显,后邊的代碼將不會(huì)再執(zhí)行。如果異常代碼沒有處理饵逐,程序?qū)⒈罎⑼顺觥?/p>

我們可以這樣處理該異常:

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

我們將調(diào)用語句放在了try 語句塊中括眠,這樣就可以捕獲異常。except 關(guān)鍵字的后邊是要捕獲的異常的名字倍权,as 后邊是捕獲到的異常 e掷豺。在except 語句塊中,我們只是將捕獲到的異常打印了出來薄声,運(yùn)行該代碼萌业,結(jié)果如下:

hello() takes 1 positional argument but 2 were given

可見錯(cuò)誤被打印了出來。我們還可以在打印錯(cuò)誤之后奸柬,再正確的調(diào)用hello 函數(shù):

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

運(yùn)行結(jié)果如下:

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

可見生年,運(yùn)行到hello('123', 'abc') 時(shí),出現(xiàn)異常廓奕,然后代碼執(zhí)行到except 語句塊抱婉,print(e) 將錯(cuò)誤打印出來,又執(zhí)行了hello('123')桌粉。

打印異常是最簡單的處理異常的方式蒸绩,在工作中,我們會(huì)將異常信息記錄在日志文件中铃肯,這樣可以將異常記錄下來患亿,以便處理異常。

一般在捕獲異常時(shí),盡量只在try 語句塊中編寫有可能發(fā)生異常的代碼步藕,基本不會(huì)發(fā)生異常的語句不要寫在try 塊中惦界,這樣可以減少try 塊中的代碼量,有助于定位問題咙冗。

except 語句

except 關(guān)鍵字后邊可以跟一個(gè)異常名字沾歪,也可以跟一組異常名字,一組異常時(shí)雾消,將多個(gè)異常的名字寫在一個(gè)元組中灾搏,語法如下:

except (Error1, Error2...):
    pass

一個(gè)try 語句中,也可以包含多個(gè)except 語句塊立润,語法如下:

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

多個(gè)except 語句塊時(shí)狂窑,Python 解釋器會(huì)從上到下依次判斷異常的類型,直到符合某個(gè)異常時(shí)桑腮,會(huì)執(zhí)行對(duì)應(yīng)的語句塊中的代碼泉哈,在該except 塊之后的except 塊將被忽略。

其中到旦,最后一個(gè)except 塊可以省略異常的名字旨巷,這種格式可以匹配任意的異常,在該塊中添忘,可以使用sys.exc_info()[0] 來獲取異常采呐。

在有多個(gè)except 語句塊時(shí),要注意搁骑,前邊的異常的范圍應(yīng)該小于等于后邊的異常的范圍斧吐,否則,后邊的except 塊將沒有意義仲器。

else 語句

Python 中煤率,try... except... 之后還可以有一個(gè)else語句塊,except 語句塊是在遇到異常時(shí)執(zhí)行的乏冀,else 語句塊是在沒有遇到異常時(shí)執(zhí)行的蝶糯。

發(fā)生異常時(shí),示例:

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

上面的代碼中辆沦,執(zhí)行到hello('123', 'abc') 時(shí)會(huì)發(fā)生異常昼捍,然后會(huì)進(jìn)入到except 語句塊,else 語句不會(huì)被執(zhí)行肢扯,執(zhí)行結(jié)果如下:

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

沒有發(fā)生異常時(shí)妒茬,示例:

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

上面代碼中的try 語句塊不會(huì)發(fā)生異常,那么except 語句就不會(huì)執(zhí)行蔚晨,else 語句會(huì)執(zhí)行乍钻,結(jié)果如下:

hello 123
沒有發(fā)生異常

注意:

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

finally 語句

finally 語句塊無論是否異常都會(huì)被執(zhí)行,該語句塊經(jīng)常用在需要關(guān)閉系統(tǒng)資源的情況下银择。

沒有發(fā)生異常時(shí)多糠,示例如下:

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

上面代碼中,try 語句塊沒有發(fā)生異常欢摄,elsefinally 都被執(zhí)行熬丧,except 語句塊沒有執(zhí)行笋粟。運(yùn)行結(jié)果如下:

hello 123
沒有發(fā)生異常
執(zhí)行了 finally 語句塊

發(fā)生異常時(shí)怀挠,示例如下:

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

上面代碼中,try 語句塊發(fā)生異常害捕,exceptfinally 都被執(zhí)行绿淋,else 語句塊沒有執(zhí)行。運(yùn)行結(jié)果如下:

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

注意:

else 語句塊與finally 語句塊可以同時(shí)存在尝盼,也可以同時(shí)不存在吞滞,也可以一個(gè)存在一個(gè)不存在,互不影響盾沫。

3裁赠,拋出異常

raise 異常

如果你捕獲了一個(gè)異常,卻不想徹底解決這個(gè)異常赴精,而想將該異常向上層拋出佩捞,可以使用raise 關(guān)鍵字。

raise 用于拋出異常蕾哟,其后可以跟一個(gè)異常對(duì)象一忱,或者什么也不跟。

raise 后跟一個(gè)異常對(duì)象

raise Exception('這里發(fā)生了錯(cuò)誤')

raise 后什么也不跟:

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

Python 解釋器會(huì)記錄最后一個(gè)發(fā)生的異常谭确,raise 會(huì)將最后一個(gè)異常拋出帘营。上面代碼中的raise 相當(dāng)于raise e

assert 斷言

assert 語句稱為斷言逐哈,就是判斷某個(gè)表達(dá)式是否為真:

  • 表達(dá)式為True 時(shí)芬迄,正常通過
  • 表達(dá)式為False 時(shí),拋出AssertionError 異常

示例如下:

表達(dá)式1 == 1True昂秃,沒有反應(yīng):

>>> assert 1 == 1

表達(dá)式1 == 0False禀梳,拋出異常:

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

4,Python 異常層次結(jié)構(gòu)

Python 異常層次結(jié)構(gòu)如下:

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

這些都是內(nèi)建異常械蹋,BaseException 是所有異常的父類出皇,我們使用最多的是Exception 及其子類』└辏可以使用help(類名)來查看每個(gè)類的詳情郊艘。

5,自定義異常

有時(shí)候,我們需要定義自己的異常類纱注,來滿足自己的需求畏浆。

我們已經(jīng)知道,Python 異常類有自己的層次結(jié)構(gòu)狞贱,所有的類都直接或者間接繼承了BaseException刻获。因此,用戶自定義的異常類瞎嬉,也需要滿足這種層次結(jié)構(gòu)蝎毡。

一般情況下,自定義異常需要繼承Exception 類氧枣。如下:

class MyError(Exception):
    pass

MyError 類的使用方式沐兵,跟內(nèi)建異常類的使用方式一樣。你可以根據(jù)自己的需要便监,為MyError 類編寫相應(yīng)的構(gòu)造方法扎谎,和其它類方法。

如果沒有為MyError 編寫構(gòu)造方法烧董,那么MyError 就繼承了Exception 的構(gòu)造方法毁靶。

6,調(diào)試錯(cuò)誤

程序編寫完成后不一定是正確的逊移,當(dāng)發(fā)現(xiàn)有錯(cuò)誤時(shí)预吆,就需要定位錯(cuò)誤的位置。

最普遍螟左,最簡單的調(diào)錯(cuò)的方法就是打印某個(gè)變量啡浊,通過輸出變量的值,來查看其是否是你想要的結(jié)果胶背。

另一種比較高效巷嚣,有力的調(diào)試代碼的方式是單步調(diào)試,即是通過設(shè)置斷點(diǎn)钳吟,深入到代碼內(nèi)部廷粒,一步一步的跟蹤查看代碼的執(zhí)行結(jié)果是否正確,從而達(dá)到修正代碼的目的红且。

C 語言中有一個(gè)非常著名的工具叫做gdb坝茎,這是一款強(qiáng)大的調(diào)試工具。Python 中也有類似的一款工具叫做pdb暇番,它使用起來要比gdb 簡單許多嗤放。

在Python 中,pdb 是一個(gè)模塊壁酬,所以次酌,在使用之前要先使用import pdb 將該模塊引入恨课。然后,在需要調(diào)試代碼的地方岳服,使用pdb.set_trace() 方法來設(shè)置斷點(diǎn)剂公,在代碼執(zhí)行到此處時(shí),Python 解釋器就會(huì)從此處開始讓你調(diào)式代碼吊宋。

如下代碼纲辽,文件名為Test.py

#! /usr/bin/env python3

# 引入 pdb 模塊
import pdb

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

# 設(shè)置斷點(diǎn)
pdb.set_trace()

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

我們使用python3 來運(yùn)行該程序,如下:

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

可以看到代碼在hello('python')之前暫停并進(jìn)入斷點(diǎn)璃搜,控制臺(tái)顯示出(Pdb)拖吼,我們可以在這個(gè)后面輸入Python 代碼或者pdb 支持的命令。

pdb 常用命令如下:

  • n:進(jìn)行下一步代碼腺劣,即單步執(zhí)行
  • c:代碼執(zhí)行到下一個(gè)斷點(diǎn)處绿贞,如果沒有下一個(gè)斷點(diǎn)因块,則執(zhí)行到程序結(jié)束
  • s:在遇到函數(shù)時(shí)橘原,使用s 命令,可以進(jìn)入函數(shù)內(nèi)部
  • l:列出當(dāng)前語句周圍的10行代碼
  • p:用于輸出變量的值涡上,相當(dāng)于print 函數(shù)

(完趾断。)

推薦閱讀:

Python 簡明教程 ---18,Python 面向?qū)ο?/a>
Python 簡明教程 ---19吩愧,Python 類與對(duì)象
Python 簡明教程 ---20芋酌,Python 類中的屬性與方法
Python 簡明教程 ---21,Python 繼承與多態(tài)
Python 簡明教程 ---22雁佳,Python 閉包與裝飾器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末脐帝,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子糖权,更是在濱河造成了極大的恐慌堵腹,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件星澳,死亡現(xiàn)場離奇詭異疚顷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)禁偎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門腿堤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人如暖,你說我怎么就攤上這事笆檀。” “怎么了盒至?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵酗洒,是天一觀的道長浸船。 經(jīng)常有香客問我,道長寝蹈,這世上最難降的妖魔是什么李命? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮箫老,結(jié)果婚禮上封字,老公的妹妹穿的比我還像新娘。我一直安慰自己耍鬓,他們只是感情好阔籽,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著牲蜀,像睡著了一般笆制。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涣达,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天在辆,我揣著相機(jī)與錄音,去河邊找鬼度苔。 笑死匆篓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的寇窑。 我是一名探鬼主播鸦概,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼甩骏!你這毒婦竟也來了窗市?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤饮笛,失蹤者是張志新(化名)和其女友劉穎咨察,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缎浇,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扎拣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了素跺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片二蓝。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖指厌,靈堂內(nèi)的尸體忽然破棺而出刊愚,到底是詐尸還是另有隱情,我是刑警寧澤踩验,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布鸥诽,位于F島的核電站商玫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏牡借。R本人自食惡果不足惜拳昌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钠龙。 院中可真熱鬧炬藤,春花似錦、人聲如沸碴里。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咬腋。三九已至羹膳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間根竿,已是汗流浹背陵像。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留犀填,地道東北人蠢壹。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像九巡,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹂季,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容