Python学习笔记:错误和异常
本文來(lái)自:入門(mén)指南
開(kāi)胃菜參考:開(kāi)胃菜
使用Python解釋器:使用Python解釋器
本文對(duì)Python的簡(jiǎn)介:Python簡(jiǎn)介
Python流程介紹:深入Python流程
Python數(shù)據(jù)結(jié)構(gòu):Python數(shù)據(jù)結(jié)構(gòu)
Python:模塊:Python模塊
Python:輸入和輸出Python輸入和輸出
目錄:
- 目錄:
- 8. 錯(cuò)誤和異常
- 8.1. 語(yǔ)法錯(cuò)誤
- 8.2. 異常
- 8.3. 異常處理
- 8.4. 拋出異常
- 8.5. 用戶自定義異常
- 8.6. 定義清理行為
- 8.7. 預(yù)定義清理行為
8. 錯(cuò)誤和異常
至今為止還沒(méi)有進(jìn)一步的談?wù)撨^(guò)錯(cuò)誤信息,不過(guò)在你已經(jīng)試驗(yàn)過(guò)的那些例子中,可能已經(jīng)遇到過(guò)一些。Python 中(至少)有兩種錯(cuò)誤:語(yǔ)法錯(cuò)誤和異常( syntax errors 和 exceptions )。
8.1. 語(yǔ)法錯(cuò)誤
語(yǔ)法錯(cuò)誤,也被稱(chēng)作解析錯(cuò)誤,也許是你學(xué)習(xí) Python 過(guò)程中最常見(jiàn)抱怨:
>>> while True print('Hello world')File "<stdin>", line 1, in ?while True print('Hello world')^ SyntaxError: invalid syntax語(yǔ)法分析器指出錯(cuò)誤行,并且在檢測(cè)到錯(cuò)誤的位置前面顯示一個(gè)小“箭頭”。 錯(cuò)誤是由箭頭 前面 的標(biāo)記引起的(或者至少是這么檢測(cè)的): 這個(gè)例子中,函數(shù) print() 被發(fā)現(xiàn)存在錯(cuò)誤,因?yàn)樗懊嫔倭艘粋€(gè)冒號(hào)( ‘:’ )。 錯(cuò)誤會(huì)輸出文件名和行號(hào),所以如果是從腳本輸入的你就知道去哪里檢查錯(cuò)誤了。
8.2. 異常
即使一條語(yǔ)句或表達(dá)式在語(yǔ)法上是正確的,當(dāng)試圖執(zhí)行它時(shí)也可能會(huì)引發(fā)錯(cuò)誤。運(yùn)行期檢測(cè)到的錯(cuò)誤稱(chēng)為 異常,并且程序不會(huì)無(wú)條件的崩潰:很快,你將學(xué)到如何在 Python 程序中處理它們。然而,大多數(shù)異常都不會(huì)被程序處理,像這里展示的一樣最終會(huì)產(chǎn)生一個(gè)錯(cuò)誤信息:
>>> 10 * (1/0) Traceback (most recent call last):File "<stdin>", line 1, in ? ZeroDivisionError: int division or modulo by zero >>> 4 + spam*3 Traceback (most recent call last):File "<stdin>", line 1, in ? NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last):File "<stdin>", line 1, in ? TypeError: Can't convert 'int' object to str implicitly錯(cuò)誤信息的最后一行指出發(fā)生了什么錯(cuò)誤。異常也有不同的類(lèi)型,異常類(lèi)型做為錯(cuò)誤信息的一部分顯示出來(lái):示例中的異常分別為 零除錯(cuò)誤( ZeroDivisionError ) ,命名錯(cuò)誤( NameError) 和 類(lèi)型錯(cuò)誤( TypeError )。打印錯(cuò)誤信息時(shí),異常的類(lèi)型作為異常的內(nèi)置名顯示。對(duì)于所有的內(nèi)置異常都是如此,不過(guò)用戶自定義異常就不一定了(盡管這是一個(gè)很有用的約定)。標(biāo)準(zhǔn)異常名是內(nèi)置的標(biāo)識(shí)(沒(méi)有保留關(guān)鍵字)。
這一行后一部分是關(guān)于該異常類(lèi)型的詳細(xì)說(shuō)明,這意味著它的內(nèi)容依賴于異常類(lèi)型。
錯(cuò)誤信息的前半部分以堆棧的形式列出異常發(fā)生的位置。通常在堆棧中列出了源代碼行,然而,來(lái)自標(biāo)準(zhǔn)輸入的源碼不會(huì)顯示出來(lái)。
內(nèi)置的異常 列出了內(nèi)置異常和它們的含義。
8.3. 異常處理
通過(guò)編程處理選擇的異常是可行的。看一下下面的例子:它會(huì)一直要求用戶輸入,直到輸入一個(gè)合法的整數(shù)為止,但允許用戶中斷這個(gè)程序(使用 Control-C 或系統(tǒng)支持的任何方法)。注意:用戶產(chǎn)生的中斷會(huì)引發(fā)一個(gè) KeyboardInterrupt 異常。
>>> while True: ... try: ... x = int(input("Please enter a number: ")) ... break ... except ValueError: ... print("Oops! That was no valid number. Try again...") ...try 語(yǔ)句按如下方式工作。
首先,執(zhí)行 try 子句 (在 try 和 except 關(guān)鍵字之間的部分)。如果沒(méi)有異常發(fā)生, except 子句 在 try 語(yǔ)句執(zhí)行完畢后就被忽略了。如果在 try 子句執(zhí)行過(guò)程中發(fā)生了異常,那么該子句其余的部分就會(huì)被忽略。如果異常匹配于 except 關(guān)鍵字后面指定的異常類(lèi)型,就執(zhí)行對(duì)應(yīng)的except子句。然后繼續(xù)執(zhí)行 try 語(yǔ)句之后的代碼。如果發(fā)生了一個(gè)異常,在 except 子句中沒(méi)有與之匹配的分支,它就會(huì)傳遞到上一級(jí) try 語(yǔ)句中。如果最終仍找不到對(duì)應(yīng)的處理語(yǔ)句,它就成為一個(gè) 未處理異常,終止程序運(yùn)行,顯示提示信息。一個(gè) try 語(yǔ)句可能包含多個(gè) except 子句,分別指定處理不同的異常。至多只會(huì)有一個(gè)分支被執(zhí)行。異常處理程序只會(huì)處理對(duì)應(yīng)的 try 子句中發(fā)生的異常,在同一個(gè) try 語(yǔ)句中,其他子句中發(fā)生的異常則不作處理。一個(gè) except 子句可以在括號(hào)中列出多個(gè)異常的名字,例如:
... except (RuntimeError, TypeError, NameError): ... pass最后一個(gè) except 子句可以省略異常名稱(chēng),以作為通配符使用。你需要慎用此法,因?yàn)樗鼤?huì)輕易隱藏一個(gè)實(shí)際的程序錯(cuò)誤!可以使用這種方法打印一條錯(cuò)誤信息,然后重新拋出異常(允許調(diào)用者處理這個(gè)異常):
import systry: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])raisetry … except 語(yǔ)句可以帶有一個(gè) else子句,該子句只能出現(xiàn)在所有 except 子句之后。當(dāng) try 語(yǔ)句沒(méi)有拋出異常時(shí),需要執(zhí)行一些代碼,可以使用這個(gè)子句。例如:
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()使用 else 子句比在 try 子句中附加代碼要好,因?yàn)檫@樣可以避免 try … except 意外的截獲本來(lái)不屬于它們保護(hù)的那些代碼拋出的異常。
發(fā)生異常時(shí),可能會(huì)有一個(gè)附屬值,作為異常的 參數(shù) 存在。這個(gè)參數(shù)是否存在、是什么類(lèi)型,依賴于異常的類(lèi)型。
在異常名(列表)之后,也可以為 except 子句指定一個(gè)變量。這個(gè)變量綁定于一個(gè)異常實(shí)例,它存儲(chǔ)在 instance.args 的參數(shù)中。為了方便起見(jiàn),異常實(shí)例定義了 _str_() ,這樣就可以直接訪問(wèn)過(guò)打印參數(shù)而不必引用 .args。這種做法不受鼓勵(lì)。相反,更好的做法是給異常傳遞一個(gè)參數(shù)(如果要傳遞多個(gè)參數(shù),可以傳遞一個(gè)元組),把它綁定到 message 屬性。一旦異常發(fā)生,它會(huì)在拋出前綁定所有指定的屬性。
>>> 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ì)于那些未處理的異常,如果一個(gè)它們帶有參數(shù),那么就會(huì)被作為異常信息的最后部分(“詳情”)打印出來(lái)。
異常處理器不僅僅處理那些在 try 子句中立刻發(fā)生的異常,也會(huì)處理那些 try 子句中調(diào)用的函數(shù)內(nèi)部發(fā)生的異常。例如:
>>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() ... except ZeroDivisionError as err: ... print('Handling run-time error:', err) ... Handling run-time error: int division or modulo by zero8.4. 拋出異常
raise 語(yǔ)句允許程序員強(qiáng)制拋出一個(gè)指定的異常。例如:
>>> raise NameError('HiThere') Traceback (most recent call last):File "<stdin>", line 1, in ? NameError: HiThere要拋出的異常由 raise 的唯一參數(shù)標(biāo)識(shí)。它必需是一個(gè)異常實(shí)例或異常類(lèi)(繼承自 Exception 的類(lèi))。
如果你需要明確一個(gè)異常是否拋出,但不想處理它,raise 語(yǔ)句可以讓你很簡(jiǎn)單的重新拋出該異常:
>>> 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 ? NameError: HiThere8.5. 用戶自定義異常
在程序中可以通過(guò)創(chuàng)建新的異常類(lèi)型來(lái)命名自己的異常(Python 類(lèi)的內(nèi)容請(qǐng)參見(jiàn) 類(lèi) )。異常類(lèi)通常應(yīng)該直接或間接的從 Exception 類(lèi)派生,例如:
>>> class MyError(Exception): ... def __init__(self, value): ... self.value = value ... def __str__(self): ... return repr(self.value) ... >>> try: ... raise MyError(2*2) ... except MyError as e: ... print('My exception occurred, value:', e.value) ... My exception occurred, value: 4 >>> raise MyError('oops!') Traceback (most recent call last):File "<stdin>", line 1, in ? __main__.MyError: 'oops!'在這個(gè)例子中,Exception 默認(rèn)的 *__init__() 被覆蓋。新的方式簡(jiǎn)單的創(chuàng)建 value 屬性。這就替換了原來(lái)創(chuàng)建 args 屬性的方式。
異常類(lèi)中可以定義任何其它類(lèi)中可以定義的東西,但是通常為了保持簡(jiǎn)單,只在其中加入幾個(gè)屬性信息,以供異常處理句柄提取。如果一個(gè)新創(chuàng)建的模塊中需要拋出幾種不同的錯(cuò)誤時(shí),一個(gè)通常的作法是為該模塊定義一個(gè)異常基類(lèi),然后針對(duì)不同的錯(cuò)誤類(lèi)型派生出對(duì)應(yīng)的異常子類(lèi):
class Error(Exception):"""Base class for exceptions in this module."""passclass InputError(Error):"""Exception raised for errors in the input.Attributes:expression -- input expression in which the error occurredmessage -- explanation of the error"""def __init__(self, expression, message):self.expression = expressionself.message = messageclass TransitionError(Error):"""Raised when an operation attempts a state transition that's notallowed.Attributes:previous -- state at beginning of transitionnext -- attempted new statemessage -- explanation of why the specific transition is not allowed"""def __init__(self, previous, next, message):self.previous = previousself.next = nextself.message = message與標(biāo)準(zhǔn)異常相似,大多數(shù)異常的命名都以 “Error” 結(jié)尾。
很多標(biāo)準(zhǔn)模塊中都定義了自己的異常,用以報(bào)告在他們所定義的函數(shù)中可能發(fā)生的錯(cuò)誤。關(guān)于類(lèi)的進(jìn)一步信息請(qǐng)參見(jiàn) 類(lèi) 一章。
8.6. 定義清理行為
try 語(yǔ)句還有另一個(gè)可選的子句,目的在于定義在任何情況下都一定要執(zhí)行的功能。例如:
>>> try: ... raise KeyboardInterrupt ... finally: ... print('Goodbye, world!') ... Goodbye, world! KeyboardInterrupt Traceback (most recent call last):File "<stdin>", line 2, in ?不管有沒(méi)有發(fā)生異常,finally子句 在程序離開(kāi) try 后都一定會(huì)被執(zhí)行。當(dāng) try 語(yǔ)句中發(fā)生了未被 except 捕獲的異常(或者它發(fā)生在 except 或 else 子句中),在 finally 子句執(zhí)行完后它會(huì)被重新拋出。 try 語(yǔ)句經(jīng)由 break ,continue 或 return 語(yǔ)句退 出也一樣會(huì)執(zhí)行 finally 子句。以下是一個(gè)更復(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 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 ?File "<stdin>", line 3, in divide TypeError: unsupported operand type(s) for /: 'str' and 'str'如你所見(jiàn), finally 子句在任何情況下都會(huì)執(zhí)行。TypeError 在兩個(gè)字符串相除的時(shí)候拋出,未被 except 子句捕獲,因此在 finally 子句執(zhí)行完畢后重新拋出。
在真實(shí)場(chǎng)景的應(yīng)用程序中,finally 子句用于釋放外部資源(文件 或網(wǎng)絡(luò)連接之類(lèi)的),無(wú)論它們的使用過(guò)程中是否出錯(cuò)。
8.7. 預(yù)定義清理行為
有些對(duì)象定義了標(biāo)準(zhǔn)的清理行為,無(wú)論對(duì)象操作是否成功,不再需要該對(duì)象的時(shí)候就會(huì)起作用。以下示例嘗試打開(kāi)文件并把內(nèi)容打印到屏幕上。
for line in open("myfile.txt"):print(line)這段代碼的問(wèn)題在于在代碼執(zhí)行完后沒(méi)有立即關(guān)閉打開(kāi)的文件。這在簡(jiǎn)單的腳本里沒(méi)什么,但是大型應(yīng)用程序就會(huì)出問(wèn)題。with 語(yǔ)句使得文件之類(lèi)的對(duì)象可以 確保總能及時(shí)準(zhǔn)確地進(jìn)行清理。
with open("myfile.txt") as f:for line in f:print(line)語(yǔ)句執(zhí)行后,文件 f 總會(huì)被關(guān)閉,即使是在處理文件中的數(shù)據(jù)時(shí)出錯(cuò)也一樣。其它對(duì)象是否提供了預(yù)定義的清理行為要查看它們的文檔。
與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的Python学习笔记:错误和异常的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: spring电子书_不懂Spring就彻
- 下一篇: websocket python爬虫_p