Python异常处理

try/except

在Python中, 使用try/except语句捕获并处理异常, 基本的语法格式如下:

try:
    可能产生异常的代码块
except [(Error1, Error2, ...) [as e]]:
    处理异常的代码块1
except [(Error3, Error4, ...) [as e]]:
    处理异常的代码块2
except [Exception]:
    处理其它异常
  • 多个except子句

可以使用多个except子句, 每个子句捕获一种类型的异常(直接在except后列出异常类型).

注解

当一个try语句后有多个except子句时, 这些except子句应该遵循这样排序规则:

可处理全部异常的except子句(参数为Exception, 或没有参数)要放到所有其它except子句的后面, 且所有父类异常的except子句要放到子类异常的except子句的后面.
  • 一箭双雕

可以使用一个except子句捕获多种类型的异常, 可在一个元组中指定这些异常.

  • 捕获异常对象

except后跟的是要捕获的异常的类型, 如果要在except子句中访问捕获的异常对象本身, 可以使用except ... as ...语句, 将捕获的异常对象绑定到指定的变量, 这样在except子句中就可以通过该变量访问捕获的异常对象.

  • 一网打尽

except子句后不指定任何异常类型, 表示捕获所有类型的异常.

注解

像这样捕获所有的异常很危险, 因为这不仅会隐藏有心里准备的错误, 还会隐藏没有考虑过的错误. 这还将捕获用户使用Ctrl + C终止执行的企图, 调用函数sys.exit来终止执行的企图的等. 在大多数情况下, 更好的情况是使用except Exception as e并对异常对象进行检查. 这样做将让不是从Exception派生而来的为数不多的异常成为漏网之鱼, 其中包括SystemExitKeyboardInterrupt, 因为它们是从BaseException(Exception的超类)派生而来的.

try except语句的执行流程如下:

  • 首先执行try中的代码块, 如果执行过程中出现异常, 系统会自动生成一个异常类型, 并将该异常提交给Python解释器, 此过程称为捕获异常;
  • 当Python解释器收到异常对象时, 会寻找能处理该异常对象的except块, 如果找到合适的except块, 则把该异常对象交给该except块处理, 这个过程称为处理异常. 如果Python解释器找不到处理异常的except块, 则程序运行终止, Python解释器也将退出.

事实上, 不管程序代码块是否处于try块中, 只要执行该代码块时出现了异常, 系统都会自动生成对应类型的异常. 但是, 如果此段代码没有用try包裹, 又或者没有为该异常类型配置处理它的except块, 则Python解释器将无法处理, 程序就会自动停止运行; 反之, 如果程序发生的异常经try捕获并由except处理完成, 则程序可以继续执行.

else

在有些情况下, 在没有出现任何异常时执行一个代码块很有用.

可以在try/except语句后添加一个else子句, 定义在没有出现任何异常时执行的代码块.

else子句, 在try语句没有捕获到任何异常时才会执行; 反之, 如果try语句捕获到异常, 则会调用对应的except子句处理异常, else子句就不会执行.

finally

finally子句, 可用于执行清理工作. 不管try子句又没有引发异常, 都将执行finally子句. 即便try子句中发生了异常, 且没有合适的except子句处理异常, finally子句仍然会得到执行.

基于finally语句的这种特性, 在某些情况下, 当try子句中打开了一些物理资源(文件, 数据库连接等)时, 由于这些资源必须手动回收, 而回收工作通常就放在finally子句中.

注解

Python垃圾回收机制, 只能帮我们回收变量, 类对象占用的内存, 而无法自动完成类似关闭文件, 数据库连接等这些工作.

raise

在Python中, 要手动引发异常, 可以使用raise语句, 并将一个类(必须是Exception的子类)或实例作为参数. 将类作为参数时, 将自动创建一个实例.

也就是说, raise语句有如下三种常用的用法:

  • raise: 单独一个raise, 该语句引发当前上下文中捕获的异常, 或默认引发RuntimeError异常;
  • raise 异常类名称: raise后带一个异常类名称, 表示引发执行类型的异常;
  • raise 异常类名称(描述信息): 在引发指定类型的异常的同时, 附带描述信息.

手动让程序引发异常, 很多时候并不是为了让程序崩溃, 而是由异常处理来捕获并处理.

获取特定异常的有关信息

每种异常类型都提供了如下几个属性和方法, 通过调用它们, 就可以获取当前处理的异常的相关信息:

  • args: 返回异常的错误编号和描述字符串;
  • str(e): 返回异常信息, 但不包括异常信息的类型;
  • repr(e): 返回较全的异常信息, 包括异常信息的类型.

Example:

try:
    1/0
except Exception as e:
    print(e.args)
    print(str(e))
    print(repr(e))

输出结果:
('division by zero',)
division by zero
ZeroDivisionError('division by zero',)