首页 > Python教程 > Python基础教程 > Python入门教程之错误和异常处理

Python入门教程之错误和异常处理

时间:2019-08-15    来源:互联网

8. 错误和异常

至今为止还没有进一步的谈论过错误信息,?#36824;?#22312;你已经试验过的那些例子中,可能已经遇到过一些。Python 中(至少)有两种错误:语法错误和异常( syntax errors 和 exceptions )。

8.1. 语法错误

语法错误,也被称作解析错误,也许是你学习 Python 过程中最常见抱怨:

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

语法分析器指出错误行,并且在检测到错误的位置前面显示一个小“箭头”。 错误是由箭头 前面 的标?#19988;?#36215;的(或者至少是这么检测的): 这个例子中,函数 print() 被发现存在错误,因为它前面少了一个冒号( ':' )。 错误会输出文件名和行号,所以如果是从脚本输入的你就知道去哪里检查错误了。

8.2. 异常

即使一条语句或表达式在语法上是正确的,当试图执行它时也可能会引发错误。运行期检测到的错误称为 异常,并且程序不会无条件的崩溃:很快,你将学到如何在 Python 程序中处理它们。然而,大多数异常都不会被程序处理,像这里展示的一样最终会产生一个错误信息:

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

错误信息的最后一行指出发生了什么错误。异常也有不同的类型,异常类型做为错误信息的一部分显示出来:示例中的异常分别为 零除错误( ZeroDivisionError ) ,命名错误( NameError) 和 类型错误( TypeError )。打印错误信息时,异常的类型作为异常的内置名显示。对于所有的内置异常都是如此,?#36824;没?#33258;定义异常就不一定了(尽管这?#19988;?#20010;很有用的约定)。标准异常名是内置的标识(没有保留关键字)。

这一行后一部分是关于该异常类型的详细说明,这意味着它的内容依赖于异常类型。

错误信息的前半部分以堆栈的形式列出异常发生的位置。通常在堆栈中列出了源代码行,然而,来自标准输入的源码不会显示出?#30784;?/p>

内置的异常 列出了内置异常和它们的含义。

8.3. 异常处理

通过编程处理选择的异常是可行的。看一下下面的例子:它会一直要求?#27809;?#36755;入,直到输入一个合法的整数为止,但允许?#27809;?#20013;断这个程序(使用 Control-C 或系统支持的任何方法)。注意:?#27809;?#20135;生的中断会引发一个 KeyboardInterrupt 异常。

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

try 语句按如下方式工作。

  • 首先,执行 try 子句 (在 try 和 except 关键字之间的部分)。

  • 如果没有异常发生, except 子句 在 try 语句执行完毕后就被忽略了。

  • 如果在 try 子句执行过程中发生了异常,那么该子句其余的部分就会被忽略。

    如果异常匹配于 except 关键字后面指定的异常类型,就执行对应的except子句。然后继续执行 try 语句之后的代码。

  • 如果发生了一个异常,在 except 子句中没有与之匹配的分支,它就会传递到上一级 try 语句?#23567;?/p>

    如果最终仍找不到对应的处理语句,它就成为一个 未处理异常,终止程序运行,显示提示信息。

一个 try 语句可能包含多个 except 子句,分别指定处理不同的异常。至多只会有一个分支被执行。异常处理程序只会处理对应的 try 子句中发生的异常,在同一个 try 语句中,其他子句中发生的异常则不作处理。一个 except 子句可以在括号中列出多个异常的名字,例如:

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

最后一个 except 子句可以省略异常名称,以作为通配符使用。你需要慎?#20040;?#27861;,因为它会轻易隐藏一个实际的程序错误!可以使用这种方法打印一条错误信息,然后重新抛出异常(允许调用者处理这个异常):

 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 语句可?#28304;?#26377;一个 else子句,该子句只能出现在所有 except 子句之后。当 try 语句没有抛出异常时,需要执行一些代码,可以使用这个子句。例如:

 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 子句中附加代码要好,因为这样可以避免 try … except 意外的截获本来不属于它们保护的那些代码抛出的异常。

发生异常时,可能会有一个附属值,作为异常的 参数 存在。这个参数是否存在、是什么类型,依赖于异常的类型。

在异常名(列表)之后,也可以为 except 子句指定一个变量。这个变量绑定于一个异常实例,它存储在 instance.args 的参数?#23567;?#20026;了方便起见,异常实例定义了 __str__() ,这样就可以直接访问过打印参数而不必引用 .args。这种做法不受鼓励。相反,更好的做法是给异常传递一个参数(如果要传递多个参数,可?#28304;?#36882;一个元组),把它绑定到 message 属性。一旦异常发生,它会在抛出前绑定所有指定的属性。

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

对于那些未处理的异常,如果一个它们带有参数,那么就会被作为异常信息的最后部分(“详情”)打印出?#30784;?/p>

异常处理器不仅仅处理那些在 try 子句中立刻发生的异常,?#19981;?#22788;理那些 try 子句中调用的函数内部发生的异常。例如:

 >>> 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. 抛出异常

raise 语句允许程序员强制抛出一个指定的异常。例如:

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

要抛出的异常由 raise 的唯一参数标识。它必需?#19988;?#20010;异常实例或异常类(继承自 Exception 的类)。

如果你需要明确一个异常是否抛出,但不想处理它,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 ?  NameError: HiThere  

8.5. ?#27809;?#33258;定义异常

在程序中可以通过创建新的异常类型来命名自己的异常(Python 类的内容请参见 类 )。异常类通常应该直接或间接的从 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 默认的 __init__() 被覆盖。新的方式简单的创建 value 属性。这就替换了原来创建 args 属性的方式。

异常类中可以定义任何其它类中可以定义的东西,但是通常为了保持简单,只在其中加入几个属性信息,?#24616;?#24322;常处理句柄提取。如果一个新创建的模块?#34892;?#35201;抛出几种不同的错误时,一个通常的作法是为该模块定义一个异常基类,然后针对不同的错误类型派生出对应的异常子类:

 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  

与标准异常相似,大多数异常的命名都以 “Error” 结尾。

很多标准模块中都定义了自己的异常,用以报告在他们所定义的函数中可能发生的错误。关于类的进一步信息请参见 类 一章。

8.6. 定义清理行为

try 语句还有另一个可选的子句,目的在于定义在任何情况下都一定要执行的功能。例如:

 >>> try:  ...     raise KeyboardInterrupt  ... finally:  ...     print('Goodbye, world!')  ...  Goodbye, world!  KeyboardInterrupt  Traceback (most recent call last):    File "<stdin>", line 2, in ?  

?#36824;?#26377;没有发生异常,finally子句 在程序离开 try 后都一定会被执行。当 try 语句中发生了未被 except 捕获的异常(或者它发生在 except 或 else 子句中),在 finally 子句执行完后它会被重新抛出。 try 语句经由 break ,continue 或 return 语句退 出也一样会执行 finally 子句。以下?#19988;?#20010;更复?#26377;?#30340;例子:

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

如你所见, finally 子句在任何情况下都会执行。TypeError 在两个字符串相除的时候抛出,未被 except 子句捕获,因此在 finally 子句执行完毕后重新抛出。

在真实场景的应用程序中,finally 子句用于?#22836;?#22806;部资源(文件 或网络连接之类的),无论它们的使用过程中是否出错。

8.7. 预定义清理行为

?#34892;?#23545;象定义了标准的清理行为,无论对象操作是否成功,不再需要该对象的时候就会起作用。以下示例尝?#28304;?#24320;文件并把内容打印到屏幕上。

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

这段代码的问题在于在代码执行完后没有立即关闭打开的文件。这在简单的脚本里没什么,但是大型应用程序就会出问题。with 语句使得文件之类的对象可以 确保总能及时准确地进行清理。

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

语句执行后,文件 f 总会被关闭,即使是在处理文件中的数据时出错也一样。其它对象是否提供了预定义的清理行为要查看它们的文档。

 

相关推荐
Python入门简介 Python能做什么?
调用Python 解释器 什么是Python 解释器?
Python3 简介
Python入门教程之流程控制
Python入门教程之数据结构
Python入门教程之模块
Python入门教程之输入和输出
Python入门教程之类
Python入门教程之标准库概览
Python入门教程之标准库浏览 – Part II
Python入门教程之虚拟环境和包
Python入门教程之浮点数算法:争议和限制

精?#37322;?#33616;

热门教程

忍者法宝试玩