午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

8. 錯(cuò)誤和異常

 xrobinson 2015-01-27

8. 錯(cuò)誤和異常?

直到現(xiàn)在,我們還沒有更多的提及錯(cuò)誤信息,但是如果你真的嘗試了前面的例子,也許你已經(jīng)見到過一些。Python(至少)有兩種錯(cuò)誤很容易區(qū)分:語法錯(cuò)誤異常。

8.1. 語法錯(cuò)誤?

語法錯(cuò)誤,或者稱之為解析錯(cuò)誤,可能是你在學(xué)習(xí) Python 過程中最煩的一種:

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

語法分析器指出了出錯(cuò)的一行,并且在最先找到的錯(cuò)誤的位置標(biāo)記了一個(gè)小小的’箭頭’。錯(cuò)誤是由箭頭前面 的標(biāo)記引起的(至少檢測(cè)到是這樣的): 在這個(gè)例子中,檢測(cè)到錯(cuò)誤發(fā)生在函數(shù)print(),因?yàn)樵谒叭鄙僖粋€(gè)冒號(hào)(':')。文件名和行號(hào)會(huì)一并輸出,所以如果運(yùn)行的是一個(gè)腳本你就知道去哪里檢查錯(cuò)誤了。

8.2. 異常?

即使一條語句或表達(dá)式在語法上是正確的,在運(yùn)行它的時(shí)候,也有可能發(fā)生錯(cuò)誤。在執(zhí)行期間檢測(cè)到的錯(cuò)誤被稱為異常 并且程序不會(huì)無條件地崩潰:你很快就會(huì)知道如何在 Python 程序中處理它們。然而大多數(shù)異常都不會(huì)被程序處理,導(dǎo)致產(chǎn)生類似下面的錯(cuò)誤信息:

>>>
>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: division 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ā)生了什么事。異常有不同的類型,其類型會(huì)作為消息的一部分打印出來:在這個(gè)例子中的類型有ZeroDivisionError NameErrorTypeError。打印出來的異常類型的字符串就是內(nèi)置的異常的名稱。這對(duì)于所有內(nèi)置的異常是正確的,但是對(duì)于用戶自定義的異常就不一定了(盡管這是非常有用的慣例)。標(biāo)準(zhǔn)異常的名稱都是內(nèi)置的標(biāo)識(shí)符(不是保留的關(guān)鍵字)。

這一行最后一部分給出了異常的詳細(xì)信息和引起異常的原因。

錯(cuò)誤信息的前面部分以堆?;厮莸男问斤@示了異常發(fā)生的上下文。通常調(diào)用棧里會(huì)包含源代碼的行信息,但是來自標(biāo)準(zhǔn)輸入的源碼不會(huì)顯示行信息。

內(nèi)置的異常 列出了內(nèi)置的異常以及它們的含義。

8.3. 拋出異常?

可以通過編程來選擇處理部分異常。看一下下面的例子,它會(huì)一直要求用戶輸入直到輸入一個(gè)合法的整數(shù)為止,但允許用戶中斷這個(gè)程序(使用Control-C或系統(tǒng)支持的任何方法);注意用戶產(chǎn)生的中斷引發(fā)的是 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í)行try 子句(tryexcept關(guān)鍵字之間的語句)。
  • 如果未發(fā)生任何異常,忽略except 子句且try語句執(zhí)行完畢。
  • 如果在 try 子句執(zhí)行過程中發(fā)生異常,跳過該子句的其余部分。如果異常的類型與except關(guān)鍵字后面的異常名匹配, 則執(zhí)行 except 子句,然后繼續(xù)執(zhí)行try語句之后的代碼。
  • 如果異常的類型與 except 關(guān)鍵字后面的異常名不匹配,它將被傳遞給上層的try語句;如果沒有找到處理這個(gè)異常的代碼,它就成為一個(gè)未處理異常,程序會(huì)終止運(yùn)行并顯示一條如上所示的信息。

Try語句可能有多個(gè)子句,以指定不同的異常處理程序。不過至多只有一個(gè)處理程序?qū)⒈粓?zhí)行。處理程序只處理發(fā)生在相應(yīng) try 子句中的異常,不會(huì)處理同一個(gè)try字句的其他處理程序中發(fā)生的異常。一個(gè) except 子句可以用帶括號(hào)的元組列出多個(gè)異常的名字,例如:

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

最后一個(gè) except 子句可以省略異常名稱,以當(dāng)作通配符使用。使用這種方式要特別小心,因?yàn)樗鼤?huì)隱藏一個(gè)真實(shí)的程序錯(cuò)誤!它還可以用來打印一條錯(cuò)誤消息,然后重新引發(fā)異常 (讓調(diào)用者也去處理這個(gè)異常):

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)時(shí),必須放在所有 except 子句的后面。如果需要在 try 語句沒有拋出異常時(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 ...保護(hù)的代碼所引發(fā)的異常。除了語句。

當(dāng)異常發(fā)生時(shí),它可能帶有相關(guān)數(shù)據(jù),也稱為異常的參數(shù)。參數(shù)的有無和類型取決于異常的類型。

except 子句可以在異常名之后指定一個(gè)變量。這個(gè)變量將綁定于一個(gè)異常實(shí)例,同時(shí)異常的參數(shù)將存放在實(shí)例的args中。為方便起見,異常實(shí)例定義了__str__() ,因此異常的參數(shù)可以直接打印而不必引用.args。也可以在引發(fā)異常之前先實(shí)例化一個(gè)異常,然后向它添加任何想要的屬性。

>>>
>>> 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ù)會(huì)作為異常信息的最后一部分打印出來。

異常處理程序不僅處理直接發(fā)生在 try 子句中的異常,而且還處理 try 子句中調(diào)用的函數(shù)(甚至間接調(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: int division or modulo by zero

8.4. 引發(fā)異常?

raise語句允許程序員強(qiáng)行引發(fā)一個(gè)指定的異常。例如:

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

raise的唯一參數(shù)指示要引發(fā)的異常。它必須是一個(gè)異常實(shí)例或異常類(從Exception派生的類)。

如果你確定需要引發(fā)異常,但不打算處理它,一個(gè)簡(jiǎn)單形式的raise語句允許你重新引發(fā)異常:

>>>
>>> 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: HiThere

8.5. 用戶定義的異常?

程序可以通過創(chuàng)建新的異常類來命名自己的異常(Python 類的更多內(nèi)容請(qǐng)參見)。異常通常應(yīng)該繼承Exception類,直接繼承或者間接繼承都可以。例如:

>>>
>>> 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!'

在此示例中,Exception默認(rèn)的__init__()被覆蓋了。新的行為簡(jiǎn)單地創(chuàng)建了value 屬性。這將替換默認(rèn)的創(chuàng)建args 屬性的行為。

異常類可以像其他類一樣做任何事情,但是通常都會(huì)比較簡(jiǎn)單,只提供一些屬性以允許異常處理程序獲取錯(cuò)誤相關(guān)的信息。創(chuàng)建一個(gè)能夠引發(fā)幾種不同錯(cuò)誤的模塊時(shí),一個(gè)通常的做法是為該模塊定義的異常創(chuàng)建一個(gè)基類,然后基于這個(gè)基類為不同的錯(cuò)誤情況創(chuàng)建特定的子類:

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

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

很多標(biāo)準(zhǔn)模塊中都定義了自己的異常來報(bào)告在它們所定義的函數(shù)中可能發(fā)生的錯(cuò)誤。 這一章給出了類的詳細(xì)信息。

8.6. 定義清理操作?

Try語句有另一個(gè)可選的子句,目的在于定義必須在所有情況下執(zhí)行的清理操作。例如:

>>>
>>> try:
...     raise KeyboardInterrupt
... finally:
...     print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt

不管有沒有發(fā)生異常,在離開try語句之前總是會(huì)執(zhí)行finally子句。當(dāng)try子句中發(fā)生了一個(gè)異常,并且沒有except字句處理(或者異常發(fā)生在tryelse子句中),在執(zhí)行完finally子句后將重新引發(fā)這個(gè)異常。try語句由于breakcontinereturn語句離開時(shí),同樣會(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.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 ?
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

正如您所看到的,在任何情況下都會(huì)執(zhí)行finally子句。由兩個(gè)字符串相除引發(fā)的 TypeError異常沒有被except子句處理,因此在執(zhí)行finally子句后被重新引發(fā)。

在真實(shí)的應(yīng)用程序中, finally子句用于釋放外部資源(例如文件或網(wǎng)絡(luò)連接),不管資源的使用是否成功。

8.7. 清理操作的預(yù)定義?

有些對(duì)象定義了在不需要該對(duì)象時(shí)的標(biāo)準(zhǔn)清理操作,無論該對(duì)象的使用是成功還是失敗。看看下面的示例,它嘗試打開一個(gè)文件并打印其內(nèi)容到屏幕。

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

這段代碼的問題就是這部分代碼執(zhí)行完之后它還會(huì)讓文件在一段不確定的時(shí)間內(nèi)保持打開狀態(tài)。這在簡(jiǎn)單的腳本中沒什么,但是在大型應(yīng)用程序中可能是一個(gè)問題。 With語句可以確保像文件這樣的對(duì)象總能及時(shí)準(zhǔn)確地被清理掉。

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

執(zhí)行該語句后,文件f 將始終被關(guān)閉,即使在處理某一行時(shí)遇到了問題。提供預(yù)定義的清理行為的對(duì)象,和文件一樣,會(huì)在它們的文檔里說明。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多