本章将学习如何创建和引发自定义的异常,以及处理异常的各种方法。 一、什么是异常 Python用异常对象(exception object)来表示异常情况。遇到错误后,会引发异常。如果异常对象并未被处理或捕捉,程序就会用所谓的回溯(Traceback,一种错误信息)终止执行。事实上,每个异常都是一些类的实例,这些实例可以被引发,并且可以用很多种方法进行捕捉,使得程序可以捉住错误并且对其进行处理,而不是让整个程序失败。 二、按自己的方式出错 异常可以在某些东西出错时自动引发。在学习如何处理异常之前,先看一下自己如何引发异常——甚至创建自己的异常类型。 1、raise语句 为了引发异常,可以使用一个类(应该是Exception的子类)或者实例参数调用raise语句。使用类时,程序会自动创建实例。 第一个例子raise Exception引发了一个没有任何有关错误信息的普通异常。后一个例子中,则添加了一些hyperdrive overload错误信息。 内建的异常类有很多。P这些内建异常都可以在exception模块(在内建的命名空间)中找到。可以使用dir函数列出模块的内容 一些最重要的内建异常类
2、自定义异常类 创建自己的异常类就像其他类一样——只是要确保从Exception类继承(不管是间接的或者是直接的,也就是说继承其他的内建异常类也是可以的) 三、捕捉异常 为了捕捉异常并且做出一些错误处理,可以这样重写程序: 如果没有捕捉到异常,它就会被“传播”到调用的函数中。如果在那里依然没有捕捉,这些异常就会“浮”到程序的最顶层。也就是说你可以捕捉到在其他人的函数中所引发的异常。 如果捕捉到了异常,但是又想重新引发它(也就是说要传递异常),那么可以调用不带参数的raise。 注:如果除零行为发生而屏蔽机制被打开,那么calc方法会(隐式地)返回None。换句话说,如果打开了屏蔽机制,那么就不应依赖返回值。 下面的例子,就是分别打开和关闭了屏蔽: 当计算器没有打开屏蔽机制时,ZeroDivisionError被捕捉但已传递了。 四、不止一个except子句 五、用一个块捕捉两个异常 如果需要用一个块捕捉多个类型异常,那么可以将它们作为元组列出 上面的代码中,如果用户输入字符串或者其他类型的值而不是数字,或者第二个数为0,都会打印同样的错误信息。 六、捕捉对象 如果希望在except子句中访问异常对象本身,可以使用两个参数(注:就算要捕捉到多个异常,也只需要向except子句提供一个参数——一个元组)。下面的示例程序会打印异常,但是程序会继续运行 七、真正的全捕捉 如果真的想用一段代码捕捉所有异常,那么可以在except子句中忽略所有的异常类: 现在可以做任何事情了 警告:像这样捕捉所有异常是危险的,因为它会隐藏所有程序员未想到并且未做好准备处理的错误。它同样会捕捉用户终止执行的Ctrl+C企图,以及用sys.exit函数终止程序的企图等等。这时用except Exception,e会更好些,或者对异常对象e进行一些检查。 八、万事大吉 有些情况中,一些坏事发生时执行一段代码是很有用的;可以像对条件循环语句那样,给try/except语句加个else子句: 使用else子句可以实现在8.5节中提到的循环: 百分之百捕捉到所有的异常是不可能的,因为try/except语句中的代码可能会出问题,比如使用旧风格的字符串异常或者自定义的异常类不是Exception类的子类。不过如果需要使用except Exception的话,可以使用上面的技巧在除法程序中打印更加有用的错误信息: 九、最后 最后是Finally子句。它可以用来在可能的异常后进行清理。它和try子句联合使用: 上面的代码中,finally子句肯定会被执行,不管try子句中是否会发生异常。运行这段代码,在程序崩溃之前,对于变量x的清理就完成了,因为使用del语句删除一个变量是非常不负责的清理手段,所以finally子句用于关闭文件或者网络套接字时会非常有用。 还可以在同一条语句中组合使用try、except、finally和else 十、异常和函数 异常和函数能很自然地一起工作。如果异常在函数内引发而不被处理,它就会传播至(浮到)函数调用的地方。 可以看到faulty中产生的异常通过faulty和ignore_exception传播,最终导致了堆栈跟踪。 十一、异常之禅 如果知道某代码可能会导致某种异常,而又不希望程序以堆栈跟踪的形式终止,那么就根据需要添加try/except或者try/finally语句进行处理。 从另一方面来看,某些程序中使用if/else实现会比使用try/except要好。假设有一个字典,希望打印出存储在特定的键下面的值。如果该键不存在,那么什么也不做。代码可以这样写: 如果给程序提供包含名字Jim Green和年龄25(没有职业)的字典的函数,会得到如下结果: 如果添加了职业“camper”,会得到如下输出: 代码非常直观,但是效率不高,解决方案如下: 这里在打印职业时使用加号而不是逗号。否则字符串'Occupation:'在异常引发之前就会被输出。 在查看对象是否存在特定特性时,try/except也很有用。假设想要看某对象是否有write特性,可以使用如下代码: 这里的try子句仅仅访问特性而不用对它做别的有用的事情。如果Attribute异常引发,就证明对象没有这个特性,反之存在该特性。 十二、本章小结 异常对象:异常情况(比如发生错误)可以用异常对象表示。它们可以用几种方法处理,但是如果忽略的话,程序就会终止。 警告:警告类似于异常,但是仅仅打印错误信息 引发异常:可以使用raise语句引发异常。它接受异常类或者异常实例作为参数。还能提供两个参数。如果在expect子句中不使用参数调用raise,它就会“重新引发”该子句捕捉到的异常。 自定义异常类:用继承Exception类的方法可以创建自己的异常类。 捕捉异常:使用try语句的except子句捕捉异常。如果在except子句中不特别指定异常类,那么所有的异常都会被捕捉。异常可以放在元组中以实现多个异常的指定 else子句:除了except子句,可以使用else子句。如果主try块中没有引发异常,else子句就会被执行 finally:如果需要确保某些代码不管是否有异常引发都要执行,那么这些代码可以放置在finally子句中。 异常和函数:在函数内引发异常时,它就会被传播到函数调用的地方。 |
|
来自: 岚风窗 > 《Python简明教程2-学习笔记》