首頁  >  文章  >  後端開發  >  Python兩種錯誤類型的介紹

Python兩種錯誤類型的介紹

零下一度
零下一度原創
2017-07-23 14:06:033584瀏覽

到現在為止都沒有過多介紹錯誤訊息,但是已經在一些範例中使用過錯誤訊息。 Python至少有兩種類型的錯誤:語法錯誤以及異常

8.1 Syntax Errors

語法錯誤,也稱為解析錯誤,是Python初學者常抱怨的問題。

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

Python解析器重複列印錯誤行,並且展示一個小箭頭,箭頭指向錯誤行中錯誤最早被偵測到的位置。錯誤由箭頭前面的語句導致(至少偵測到是這樣的):以上範例中,錯誤在print()函數被偵測到,因為在它之前的冒號:缺失了。檔案名稱和行號也被印出來,當程式來自腳本時可以方便定位問題。

8.2 Exceptions

即使語句或表達式在語法上是正確的,但是在執行的時候也可能會發生錯誤。在執行期間偵測到的問題叫做異常,異常不是絕對的致命錯誤:接下來會介紹如何在Python中處理異常。然而大多數異常都不會被程式處理,它們最終會成為錯誤訊息,展示如下:

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

最後一行錯誤訊息指出發生了什麼。異常有不同類型,類型也作為錯誤訊息的一部分列印出來:上述範例中的類型有除零異常ZeroDivisionError, 名字異常NameError 以及類型異常TypeError。作為異常類型打印的字串是built-in異常的名字。對於所有built-in異常都是如此,儘管這個約定很有用,但是並不是所有使用者自訂異常都會遵守。標準異常名字是built-in標識符(不是保留字)。

剩餘行基於異常類型展示了詳細的資訊以及異常發生的原因。

前面部分錯誤訊息以堆疊回溯的方式,展示異常發生的上下文資訊。通常在堆疊回溯中列出了原始程式碼行;然而,當程式是從標準輸入中讀取時,不會展示行。

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

8.3 Handling Exceptions

Python程式允許處理指定例外狀況。以下程式要求使用者循環輸入,直到輸入為有效整數停止,但是也可以中斷程式(使用Control-C或其他作業系統支援的手段);注意使用者誘發的中斷會拋出KeyboardInterrupt例外。

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

try語句的執行順序為:

  • 首先,執行在tryexcept之間的try子句

  • 如果沒有例外發生,跳過except子句try語句執行完畢

  • 如果try子句執行期間有例外發生,則該子句內剩餘語句被跳過。接下來如果拋出的異常類型可以與關鍵字except後的異常匹配,那麼except子句執行,接著繼續執行try語句後的語句。

  • 如果異常發生但是沒有匹配到except後的異常,那麼異常拋到外層try語句;如果異常沒有處理,該異常成為未處理異常,導致程式終止並且像上述範例一樣列印資訊。

一個try語句可以有多個except子句,為不同的例外指定處理方法。至多只有一個except子句會被執行。處理程序只會處理發生在對應try子句中的異常,而不會處理發生在同一try語句中其他處理程序中的異常。一個except子句可以使用括號的元組列出多個異常,就像這樣:

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

except子句中的類別可以匹配類型是其子類別或它自己類型的異常(但反過來不行,except子句中的子類別不會符合父類別異常)。例如以下程式碼會依序列印B, C, D:

class B(Exception):passclass C(B):passclass D(C):passfor cls in [B, C, D]:try:raise cls()except D:print("D")except C:print("C")except B:print("B")

注意以上的except子句若反過來寫(except B在前),那麼將會印出B, B, B ——第一個except子句的匹配被觸發。

最後一個except子句可以省略異常名字作為萬用字元。因為這樣做會隱藏其他的真實程式錯誤,所以所有要謹慎使用。也可以用來列印錯誤訊息並且重新拋出(允許呼叫者處理例外):

import systry:
    f = open(&#39;myfile.txt&#39;)
    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語句有一個可選的else子句,該子句只能出現在所有except子句之後。若要求try子句沒有拋出例外時必須執行一段程式碼,使用else子句很有用(譯註:else子句會被return, break, continue跳過)。例如:

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

使用 else 子句比在 try 子句中附加代码要好,因为这样可以避免 try ... except 意外的捕获的本不属于它们保护的那些代码抛出的异常。

异常发生时,可以携带与之关联的值,也称作异常参数。异常参数是否可以存在以及其类型取决于异常类型。

except子句可以在异常名字后指定变量。该变量绑定到异常实例,异常参数存储在instance.args属性中。方便起见,异常实例定义了__str__()以便异常参数可以直接打印,而不是使用.args引用。也可以首先实例化异常,并在抛出它之前加入任何想要的参数:

>>> try:
...     raise Exception(&#39;spam&#39;, &#39;eggs&#39;)
... 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(&#39;x =&#39;, x)
...     print(&#39;y =&#39;, y)
...<class &#39;Exception&#39;>(&#39;spam&#39;, &#39;eggs&#39;)
(&#39;spam&#39;, &#39;eggs&#39;)
x = spam
y = eggs

对于未处理异常来说,如果它有参数,那么参数将在错误信息的后面部分(详细信息部分)打印。

异常处理程序不仅仅会处理即时发生在try子句中的异常,还会处理try子句中直接或者间接调用的函数中发生的异常。例如:

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

8.4 Raising Exceptions

raise语句允许程序员强制发生异常。例如:

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

raise的唯一参数指定要抛出的异常。参数必须是异常实例或者异常类(继承自Exception类的子类)。如果参数是异常类型,会隐式使用无参方式调用异常的构造器初始化一个异常实例:

raise ValueError  # shorthand for &#39;raise ValueError()&#39;

如果需要捕获异常但是不处理,一种更简单形式的raise语句允许重新抛出这个异常:

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

8.5 User-defined Exception

程序中可以通过创建新异常类的方式提出自己的异常(参见Classes获取Python类的更多信息)。异常必须直接或者间接继承自Exception类。

自定义异常类拥有其他类的功能,但通常需要保持其简洁性,只提供几个供异常处理程序提取错误信息的属性。需要创建一个抛出若干不同错误的模块时,比较好的实践是,为定义在这个模块中的异常创建父类,由子类创建对应不同错误的具体异常:

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 occurred        message -- 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&#39;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 = previousself.next = nextself.message = message

与标准异常类类似,大多数异常的名字都以"Error"结尾。

许多标准模块都定义了自己的异常,这些异常对应模块中定义的函数中可能发生的错误。更多信息参考Classes。

8.6 Defining Clean-up Actions

try语句有可选的在任何情况下都会执行的子句,可用于定义清理动作。例如:

>>> try:
...     raise KeyboardInterrupt... finally:
...     print(&#39;Goodbye, world!&#39;)
...
Goodbye, world!KeyboardInterruptTraceback (most recent call last):
  File "<stdin>", line 2, in <module>

无论是否有异常发生,finally子句在离开try语句之前总是会执行。当try子句中有异常发生并且没有被except子句处理(或者异常发生在except子句或else子句),finally子句执行之后,这些异常会重新抛出。当try语句的其他子句通过break, continue或者return语句离开时,finally子句也会执行。以下是较为复杂的示例:

>>> 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.0executing 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 divideTypeError: unsupported operand type(s) for /: &#39;str&#39; and &#39;str&#39;

正如所见的那样,finally子句在任何情况下都会执行。两个字符串相除产生的TypeError异常没有被except子句处理,因此在finally子句执行完毕之后被重新抛出。

在实践应用中,finally子句用来释放外部资源(比如文件和网络连接),不论资源的使用是否成功。

8.7 Predefined Clean-up Actions

一些对象定义了标准的清理动作,当不再需要这些对象时,会执行清理动作,而不论使用对象的操作是否成功。以下示例读取文件并打印内容到显示器:

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

这段代码的问题是:当代码执行完毕后,文件会保留打开状态一段不确定的时间。这在简单的脚本中不是什么大问题,但是在大型的应用程序中会出问题。with语句使得像文件一样的对象可以被立即准确回收。

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

语句执行后,即使在执行代码时遇到问题,文件f总是会被关闭。像文件一样提供预定义清理动作的对象会在其说明文档中指示这点。

以上是Python兩種錯誤類型的介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn