错误处理(error handling)和异常 |
如果在程序运行中发生了错误,Python 解释器将输出错误信息和对执行栈的追踪信息。 在交互式执行的模式下,显示错误信息后,解释器返回到基本提示符状态。 在 IDLE 里,如果是执行正在编辑的文件里的程序,解释器输出信息后回到交互状态。 如果执行的程序来自文件,解释器在输出上述信息之后结束本次文件的执行。 在程序执行中输入中断字符(ctrl-C),可以使解释器中断对程序的执行并回到基本提示符状态。(例如遇到死循环,或希望中断正在执行的程序,不希望继续计算)。 异常(exception)解释器可能检查程序执行中发生的一些错误,这些错误都不是致命,Python 称它们为异常。Python 有特殊的用于处理异常的结构,可以利用这些结构,让程序在完成对异常的处理后回到正常工作状态,继续执行。Python 还提供了定义异常和引发异常的机制,我们可以根据需要,利用它们描述一些特殊的执行流程。 如果发生了异常,程序里又没有处理,正在执行的程序就会以异常的方式终止,系统输出有关的错误信息。在交互状态下看到的动态运行错误信息就是这种情况。 下面是 Python 教程里的几个例子: >>> 10 * (1/0) Traceback (most recent call last): File "第一个错误是遇到除数为 0 的情况;第二个遇到变量没定义(因此没有值);第三个是运算对象的类型错,系统无法做字符串和整数的加法(不会自动将整数转换到字符串)。错误信息里出现的几个表示错误情况的名字(ZeroDivisionError, NameError, TypeError)就称为 "异常名",这是一些语言内部定义的异常。 每个异常是一个特殊的类型,标准库手册第5节列出了 Python 系统的所有内部定义异常。 异常处理(exception handling)如果预见到一段程序的执行中可能发生某些异常,而且存在合适的方式处理这些(或者是其中的一种或几种)异常,那么就可以写处理异常的程序段。Python 里为处理异常提供的结构是 try 语句。假定原来的可能发生异常的程序段是 code1,可能发生的一个异常的名字是 ex1,处理这个异常的代码段是 code2,可以用下面程序段完成有关的处理流程: try : code1 except ex1 : code2程序执行这段代码执行时,解释器立即去执行 code1。如果 code1 的执行中没出现异常,它的执行完成也就是上述整个代码段的执行完成。如果 code1 执行中发生了异常 ex1,执行立刻转入代码段 code2(对 code1 的执行立刻终止,无论当时执行到 code1 中哪个地方),code2 的执行完成就是这个代码段的执行完成。 上面给出的是最简单的情况。实际上
异常的引发(raise 语句)可以在程序的代码里主动引发异常,Python 为引发异常提供了专门的 raise 语句,其简单使用形式是:raise 异常名程序执行到这个语句将导致指定的异常发生。 如果在 try 语句的异常处理器里使用 raise 语句,可以不写 "异常名",表示引发当时捕捉到的且正在处理的那个异常。显然,只有在发生了一个异常时,执行才可能到达与之对应的 except 块的异常处理器。由于一个 except 段可能处理多个异常(例如它处理的异常用用一个元组描述),写不带异常名的 raise 语句可以保证再次引发的仍然是当时捕捉到并正在处理的那个异常。 下面是教程里给出的一个示例: import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except IOError as err: print("I/O error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise第一个 except 块处理 IOError(与输入输出有关的异常),例如文件不能正常打开等。这是一个内部定义异常,发生这种异常时将触发第一个异常处理器,它输出相应信息后结束。 如果文件正常打开,但读入的一行不能转换为整数,就会引发一个 ValueError 异常,触发第二个异常处理器。 如果在 try 语句基本代码段的执行中发生其他异常,就会触发第三个异常处理器。该处理器输出一条错误信息后再次引发当时发生的异常,把它传播出去。如果上面这段代码是写在一个函数里,该异常就会继续传到调用这个函数的代码段,那里可以对发生的异常做进一步处理。 try 语句的 else 段在一个 try 语句的所有 except 块之后可以有一个 else 块。如果在这个 try 语句的基本代码段执行中没有出现异常,该代码执行完成后就执行这个 else 段里的语句。下面是一个例子: for fname in flist : try: f = open(fname, 'r') usefile(f) except IOError: print('cannot open', fname) else: print('File ', fname, ' have been processed.') f.close()这里假定 flist 里是一组表示文件名的字符串,我们需要一个个处理这些文件的内容,函数 usefile 完成对一个文件的内容的处理。如果文件正常打开并处理,程序的执行将进入最后的 else 段,输出一条信息并关闭文件。如果文件不能正常打开,执行中发生异常,异常处理器输出一条说明文件有问题的信息。无论怎样,循环都将继续到处理完 flist 列出的所有名字。 finally 段try 语句还可以有一个 finally 段,用于描述这个 try 语句结束前必须执行的操作。无论该 try 语句的执行中是否出现异常,是否有异常处理器被执行,在异常处理器的执行中是否出现异常,finally 段里的语句都会执行。注意:无论一个 try 语句有没有 finally 段,都不影响它的正常或异常终止状态。如果在执行 final 段之前执行处于正常状态,执行正常进入 finally 段,这个段结束时的状态(正常或异常)决定整个 try 语句的结束时的状态。如果 try 语句的执行以异常状态进入 finally 段,这个 try 语句必定以异常状态结束。 人们通常在 finally 段里写一些清理操作,以保证跟在 try 语句后面的代码能正常执行。例子参看教程的8.6节。 有关正确使用异常处理机制的技术和方法,参见课堂讨论和其他参考材料。 |
本页及相关页面(除另声明者外)由裘宗燕创建维护,可自由用于各种学习活动。其他使用需得到作者许可。 |