[譯]The Python Tutorial#Errors and Exceptions

[譯]The Python Tutorial#Errors and Exceptions

到現(xiàn)在為止都沒有過多介紹錯(cuò)誤信息献汗,但是已經(jīng)在一些示例中使用過錯(cuò)誤信息。Python至少有兩種類型的錯(cuò)誤:語法錯(cuò)誤以及異常

8.1 Syntax Errors

語法錯(cuò)誤,也稱解析錯(cuò)誤,是Python初學(xué)者經(jīng)常抱怨的問題。

>>> while True print('Hello world')
  File "<stdin>", line 1
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax

Python解析器重復(fù)打印錯(cuò)誤行弦追,并且展示一個(gè)小箭頭,箭頭指向錯(cuò)誤行中錯(cuò)誤最早被偵測到的位置花竞。錯(cuò)誤由箭頭前面的語句導(dǎo)致(至少偵測到是這樣的):以上示例中劲件,錯(cuò)誤在print()函數(shù)被偵測到,因?yàn)樵谒暗拿疤?hào):缺失了约急。文件名和行號(hào)也被打印出來零远,當(dāng)程序來自腳本時(shí)可以方便定位問題。

8.2 Exceptions

即使語句或者表達(dá)式在語法上是正確的厌蔽,但是在執(zhí)行的時(shí)候也可能會(huì)發(fā)生錯(cuò)誤牵辣。在執(zhí)行期間偵測到的問題叫做異常,異常不是絕對(duì)的致命錯(cuò)誤:接下來會(huì)介紹如何在Python中處理異常奴饮。然而大多數(shù)異常都不會(huì)被程序處理纬向,它們最終會(huì)成為錯(cuò)誤信息择浊,展示如下:

>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly

最后一行錯(cuò)誤信息指出發(fā)生了什么。異常有不同類型逾条,類型也作為錯(cuò)誤信息的一部分打印出來:以上示例中的類型有除零異常ZeroDivisionError, 名字異常NameError 以及類型異常TypeError近她。作為異常類型打印的字符串是built-in異常的名字。對(duì)于所有built-in異常來說都是如此膳帕,盡管這個(gè)約定很有用,但是并不是所有用戶自定義異常都會(huì)遵守薇缅。標(biāo)準(zhǔn)異常名字是built-in標(biāo)識(shí)符(不是保留字)危彩。

剩余行基于異常類型展示了詳細(xì)的信息以及異常發(fā)生的原因。

前面部分錯(cuò)誤信息以堆椨捐耄回溯的方式汤徽,展示異常發(fā)生的上下文信息。通常堆椌淖回溯中列出了源代碼行谒府;然而,當(dāng)程序是從標(biāo)準(zhǔn)輸入中讀取時(shí)浮毯,不會(huì)展示行完疫。

Built-in異常列舉了所有built-in異常以及它們的意義。

8.3 Handling Exceptions

Python程序允許處理指定異常债蓝。以下程序要求用戶循環(huán)輸入壳鹤,直到輸入為有效整數(shù)停止,但是也可以中斷程序(使用Control-C或者其他操作系統(tǒng)支持的手段)饰迹;注意用戶誘發(fā)的中斷會(huì)拋出KeyboardInterrupt異常芳誓。

while True:
    try:
        x = int(input("Please enter a number: "))
        break
    except ValueError:
        print("Oops!  That was no valid number.  Try again...")

try語句的執(zhí)行順序?yàn)椋?/p>

  • 首先,執(zhí)行在tryexcept之間的try子句
  • 如果沒有異常發(fā)生啊鸭,跳過except子句锹淌,try語句執(zhí)行完畢
  • 如果try子句執(zhí)行期間有異常發(fā)生,該子句內(nèi)剩余語句被跳過赠制。接下來如果拋出的異常類型可以與關(guān)鍵字except后的異常匹配赂摆,那么except子句執(zhí)行,接著繼續(xù)執(zhí)行try語句后的語句钟些。
  • 如果異常發(fā)生但是沒有匹配到except后的異常库正,那么異常拋到外層try語句;如果異常得不到處理厘唾,該異常成為未處理異常褥符,導(dǎo)致程序終止并且像以上示例一樣打印信息。

一個(gè)try語句可以有多個(gè)except子句抚垃,為不同的異常指定處理方法喷楣。至多只有一個(gè)except子句會(huì)被執(zhí)行趟大。處理程序只會(huì)處理發(fā)生在對(duì)應(yīng)try子句中的異常,而不會(huì)處理發(fā)生在同一try語句中其他處理程序中的異常铣焊。一個(gè)except子句可以使用帶括號(hào)的元組列出多個(gè)異常逊朽,就像這樣:

... except (RuntimeError, TypeError, NameError):
...     pass

except子句中的類可以匹配類型是其子類或者它自己類型的異常(但反過來不行,except子句中的子類不會(huì)匹配父類異常)曲伊。例如以下代碼會(huì)依次打印B, C, D:

class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

注意以上的except子句若反過來寫(except B在前)叽讳,那么將會(huì)打印B, B, B ——第一個(gè)except子句的匹配被觸發(fā)。

最后一個(gè)except子句可以省略異常名字作為通配符坟募。因?yàn)檫@樣做會(huì)隱藏其他的真實(shí)程序錯(cuò)誤岛蚤,所有要謹(jǐn)慎使用。也可以用來打印錯(cuò)誤信息并且重新拋出(允許調(diào)用者處理異常):

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

try...except語句有一個(gè)可選的else子句懈糯,該子句只能出現(xiàn)在所有except子句之后涤妒。若要求try子句沒有拋出異常時(shí)必須執(zhí)行一段代碼,使用else子句很有用(譯注:else子句會(huì)被return, break, continue跳過)赚哗。例如:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

使用 else 子句比在 try 子句中附加代碼要好她紫,因?yàn)檫@樣可以避免 try ... except 意外的捕獲的本不屬于它們保護(hù)的那些代碼拋出的異常。

異常發(fā)生時(shí)屿储,可以攜帶與之關(guān)聯(lián)的值贿讹,也稱作異常參數(shù)。異常參數(shù)是否可以存在以及其類型取決于異常類型够掠。

except子句可以在異常名字后指定變量围详。該變量綁定到異常實(shí)例,異常參數(shù)存儲(chǔ)在instance.args屬性中祖屏。方便起見助赞,異常實(shí)例定義了__str__()以便異常參數(shù)可以直接打印,而不是使用.args引用袁勺。也可以首先實(shí)例化異常雹食,并在拋出它之前加入任何想要的參數(shù):

>>> try:
...     raise Exception('spam', 'eggs')
... except Exception as inst:
...     print(type(inst))    # the exception instance
...     print(inst.args)     # arguments stored in .args
...     print(inst)          # __str__ allows args to be printed directly,
...                          # but may be overridden in exception subclasses
...     x, y = inst.args     # unpack args
...     print('x =', x)
...     print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

對(duì)于未處理異常來說,如果它有參數(shù)期丰,那么參數(shù)將在錯(cuò)誤信息的后面部分(詳細(xì)信息部分)打印群叶。

異常處理程序不僅僅會(huì)處理即時(shí)發(fā)生在try子句中的異常,還會(huì)處理try子句中直接或者間接調(diào)用的函數(shù)中發(fā)生的異常钝荡。例如:

>>> def this_fails():
...     x = 1/0
...
>>> try:
...     this_fails()
... except ZeroDivisionError as err:
...     print('Handling run-time error:', err)
...
Handling run-time error: division by zero

8.4 Raising Exceptions

raise語句允許程序員強(qiáng)制發(fā)生異常街立。例如:

>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: HiThere

raise的唯一參數(shù)指定要拋出的異常。參數(shù)必須是異常實(shí)例或者異常類(繼承自Exception類的子類)埠通。如果參數(shù)是異常類型具篇,會(huì)隱式使用無參方式調(diào)用異常的構(gòu)造器初始化一個(gè)異常實(shí)例:

raise ValueError  # shorthand for 'raise ValueError()'

如果需要捕獲異常但是不處理略步,一種更簡單形式的raise語句允許重新拋出這個(gè)異常:

>>> try:
...     raise NameError('HiThere')
... except NameError:
...     print('An exception flew by!')
...     raise
...
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: HiThere

8.5 User-defined Exception

程序中可以通過創(chuàng)建新異常類的方式提出自己的異常(參見Classes獲取Python類的更多信息)。異常必須直接或者間接繼承自Exception類芹务。

自定義異常類擁有其他類的功能翠桦,但通常需要保持其簡潔性,只提供幾個(gè)供異常處理程序提取錯(cuò)誤信息的屬性。需要?jiǎng)?chuàng)建一個(gè)拋出若干不同錯(cuò)誤的模塊時(shí),比較好的實(shí)踐是码撰,為定義在這個(gè)模塊中的異常創(chuàng)建父類,由子類創(chuàng)建對(duì)應(yīng)不同錯(cuò)誤的具體異常:

class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

與標(biāo)準(zhǔn)異常類類似个盆,大多數(shù)異常的名字都以"Error"結(jié)尾脖岛。

許多標(biāo)準(zhǔn)模塊都定義了自己的異常,這些異常對(duì)應(yīng)模塊中定義的函數(shù)中可能發(fā)生的錯(cuò)誤颊亮。更多信息參考Classes柴梆。

8.6 Defining Clean-up Actions

try語句有可選的在任何情況下都會(huì)執(zhí)行的子句,可用于定義清理動(dòng)作编兄。例如:

>>> try:
...     raise KeyboardInterrupt
... finally:
...     print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>

無論是否有異常發(fā)生,finally子句在離開try語句之前總是會(huì)執(zhí)行声登。當(dāng)try子句中有異常發(fā)生并且沒有被except子句處理(或者異常發(fā)生在except子句或else子句)狠鸳,finally子句執(zhí)行之后,這些異常會(huì)重新拋出悯嗓。當(dāng)try語句的其他子句通過break, continue或者return語句離開時(shí)件舵,finally子句也會(huì)執(zhí)行。以下是較為復(fù)雜的示例:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

正如所見的那樣脯厨,finally子句在任何情況下都會(huì)執(zhí)行铅祸。兩個(gè)字符串相除產(chǎn)生的TypeError異常沒有被except子句處理,因此在finally子句執(zhí)行完畢之后被重新拋出合武。

在實(shí)踐應(yīng)用中临梗,finally子句用來釋放外部資源(比如文件和網(wǎng)絡(luò)連接),不論資源的使用是否成功稼跳。

8.7 Predefined Clean-up Actions

一些對(duì)象定義了標(biāo)準(zhǔn)的清理動(dòng)作盟庞,當(dāng)不再需要這些對(duì)象時(shí),會(huì)執(zhí)行清理動(dòng)作汤善,而不論使用對(duì)象的操作是否成功什猖。以下示例讀取文件并打印內(nèi)容到顯示器:

for line in open("myfile.txt"):
    print(line, end="")

這段代碼的問題是:當(dāng)代碼執(zhí)行完畢后,文件會(huì)保留打開狀態(tài)一段不確定的時(shí)間红淡。這在簡單的腳本中不是什么大問題不狮,但是在大型的應(yīng)用程序中會(huì)出問題。with語句使得像文件一樣的對(duì)象可以被立即準(zhǔn)確回收在旱。

with open("myfile.txt") as f:
    for line in f:
        print(line, end="")

語句執(zhí)行后摇零,即使在執(zhí)行代碼時(shí)遇到問題,文件f總是會(huì)被關(guān)閉桶蝎。像文件一樣提供預(yù)定義清理動(dòng)作的對(duì)象會(huì)在其說明文檔中指示這點(diǎn)遂黍。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末终佛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子雾家,更是在濱河造成了極大的恐慌铃彰,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,946評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芯咧,死亡現(xiàn)場離奇詭異牙捉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)敬飒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門邪铲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人无拗,你說我怎么就攤上這事带到。” “怎么了英染?”我有些...
    開封第一講書人閱讀 169,716評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵揽惹,是天一觀的道長。 經(jīng)常有香客問我四康,道長搪搏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,222評(píng)論 1 300
  • 正文 為了忘掉前任闪金,我火速辦了婚禮疯溺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哎垦。我一直安慰自己囱嫩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,223評(píng)論 6 398
  • 文/花漫 我一把揭開白布漏设。 她就那樣靜靜地躺著挠说,像睡著了一般。 火紅的嫁衣襯著肌膚如雪愿题。 梳的紋絲不亂的頭發(fā)上损俭,一...
    開封第一講書人閱讀 52,807評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音潘酗,去河邊找鬼杆兵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛仔夺,可吹牛的內(nèi)容都是我干的琐脏。 我是一名探鬼主播,決...
    沈念sama閱讀 41,235評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼日裙!你這毒婦竟也來了吹艇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,189評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤昂拂,失蹤者是張志新(化名)和其女友劉穎受神,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體格侯,經(jīng)...
    沈念sama閱讀 46,712評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鼻听,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,775評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了联四。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撑碴。...
    茶點(diǎn)故事閱讀 40,926評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖朝墩,靈堂內(nèi)的尸體忽然破棺而出醉拓,到底是詐尸還是另有隱情,我是刑警寧澤收苏,帶...
    沈念sama閱讀 36,580評(píng)論 5 351
  • 正文 年R本政府宣布亿卤,位于F島的核電站,受9級(jí)特大地震影響倒戏,放射性物質(zhì)發(fā)生泄漏怠噪。R本人自食惡果不足惜恐似,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,259評(píng)論 3 336
  • 文/蒙蒙 一杜跷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧矫夷,春花似錦葛闷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至忧陪,卻和暖如春扣泊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嘶摊。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評(píng)論 1 274
  • 我被黑心中介騙來泰國打工延蟹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叶堆。 一個(gè)月前我還...
    沈念sama閱讀 49,368評(píng)論 3 379
  • 正文 我出身青樓阱飘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沥匈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,930評(píng)論 2 361

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