到現在為止都沒有過多介紹錯誤訊息,但是已經在一些範例中使用過錯誤訊息。 Python至少有兩種類型的錯誤:語法錯誤以及異常
語法錯誤,也稱為解析錯誤,是Python初學者常抱怨的問題。
>>> while True print('Hello world') File "<stdin>", line 1while True print('Hello world') ^SyntaxError: invalid syntax
Python解析器重複列印錯誤行,並且展示一個小箭頭,箭頭指向錯誤行中錯誤最早被偵測到的位置。錯誤由箭頭前面的語句導致(至少偵測到是這樣的):以上範例中,錯誤在print()
函數被偵測到,因為在它之前的冒號:
缺失了。檔案名稱和行號也被印出來,當程式來自腳本時可以方便定位問題。
即使語句或表達式在語法上是正確的,但是在執行的時候也可能會發生錯誤。在執行期間偵測到的問題叫做異常,異常不是絕對的致命錯誤:接下來會介紹如何在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 'spam' is not defined>>> '2' + 2Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: Can't convert 'int' object to str implicitly
最後一行錯誤訊息指出發生了什麼。異常有不同類型,類型也作為錯誤訊息的一部分列印出來:上述範例中的類型有除零異常ZeroDivisionError
, 名字異常NameError
以及類型異常TypeError
。作為異常類型打印的字串是built-in異常的名字。對於所有built-in異常都是如此,儘管這個約定很有用,但是並不是所有使用者自訂異常都會遵守。標準異常名字是built-in標識符(不是保留字)。
剩餘行基於異常類型展示了詳細的資訊以及異常發生的原因。
前面部分錯誤訊息以堆疊回溯的方式,展示異常發生的上下文資訊。通常在堆疊回溯中列出了原始程式碼行;然而,當程式是從標準輸入中讀取時,不會展示行。
Built-in異常列舉了所有built-in異常以及它們的意義。
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
語句的執行順序為:
首先,執行在try
和except
之間的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('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
語句有一個可選的else子句,該子句只能出現在所有except子句之後。若要求try子句沒有拋出例外時必須執行一段程式碼,使用else子句很有用(譯註:else
子句會被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
子句中附加代码要好,因为这样可以避免 try
... except
意外的捕获的本不属于它们保护的那些代码抛出的异常。
异常发生时,可以携带与之关联的值,也称作异常参数。异常参数是否可以存在以及其类型取决于异常类型。
except子句可以在异常名字后指定变量。该变量绑定到异常实例,异常参数存储在instance.args
属性中。方便起见,异常实例定义了__str__()
以便异常参数可以直接打印,而不是使用.args
引用。也可以首先实例化异常,并在抛出它之前加入任何想要的参数:
>>> 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
对于未处理异常来说,如果它有参数,那么参数将在错误信息的后面部分(详细信息部分)打印。
异常处理程序不仅仅会处理即时发生在try子句中的异常,还会处理try子句中直接或者间接调用的函数中发生的异常。例如:
>>> 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
raise语句允许程序员强制发生异常。例如:
>>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in <module>NameError: HiThere
raise
的唯一参数指定要抛出的异常。参数必须是异常实例或者异常类(继承自Exception类的子类)。如果参数是异常类型,会隐式使用无参方式调用异常的构造器初始化一个异常实例:
raise ValueError # shorthand for 'raise ValueError()'
如果需要捕获异常但是不处理,一种更简单形式的raise
语句允许重新抛出这个异常:
>>> 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
程序中可以通过创建新异常类的方式提出自己的异常(参见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'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。
try
语句有可选的在任何情况下都会执行的子句,可用于定义清理动作。例如:
>>> try: ... raise KeyboardInterrupt... finally: ... print('Goodbye, world!') ... 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 /: 'str' and 'str'
正如所见的那样,finally
子句在任何情况下都会执行。两个字符串相除产生的TypeError
异常没有被except
子句处理,因此在finally
子句执行完毕之后被重新抛出。
在实践应用中,finally
子句用来释放外部资源(比如文件和网络连接),不论资源的使用是否成功。
一些对象定义了标准的清理动作,当不再需要这些对象时,会执行清理动作,而不论使用对象的操作是否成功。以下示例读取文件并打印内容到显示器:
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中文網其他相關文章!