36.异常处理

李光朱课程 2024-03-25 14:46:59
什么是异常

本节开始介绍之前,先看看如下程序:

>>> print(a)Traceback (most recent call last): File "<stdin>", line 1, in <module>NameError: name 'a' is not defined>>>

是不是很熟悉,这是我们前面经常看到的程序运行出现的错误。

作为Python初学者,在学习Python编程的过程中,经常会看到一些报错信息,使你编写的程序不能如期工作,如我们前面看到过的NameError、SyntaxError、TypeError、ValueError等,这些都是异常。

异常是一个事件,该事件会在程序执行过程中发生,影响程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生异常。异常是Python的对象,表示一个错误。当Python脚本发生异常时,我们需要捕获并处理异常,否则程序会终止执行。

每一个异常都是一些类的实例,这些实例可以被引用,并且可以用很多种方法进行捕捉,使得错误可以被处理,而不是让整个程序失败。

异常处理

出现异常怎么办呢?

就如我们使用的工具出了点小毛病,我们可以想办法修理好它。程序也一样,前辈们经过不断积累与思考,创造了不少好方法处理程序中的异常,最简单的是使用try语句处理。

try语句的基本形式为try / except。try / except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。如果你不想在发生异常时结束程序,只需在try语句块中捕获异常即可。

捕获异常的语法如下:

try: <语句>  #运行别的代码except <名字>: <语句>  #如果在try部分引发了异常

try的工作原理是,开始一个try语句后,Python就在当前程序的上下文中做标记,当出现异常时就可以回到做标记的地方。首先执行try子句,接下来发生什么依赖于执行时是否出现异常。

如果try后的语句执行时发生异常,程序就跳回try并执行except子句。异常处理完毕后,控制流就可以通过整个try语句了(除非在处理异常时又引发新异常)。

例如以下示例所示(exp_exception.py):

def exp_exception(x, y): try: result = x / y print('计算结果: ', result) except: print('程序出错: 除数不能为零')

程序执行结果如下:

程序出错: 除数不能为零

由执行结果看到,程序最后执行的是except子句,如果语句正常,应该打印name变量的值。

抛出异常

Python使用raise语句抛出一个指定异常。我们可以使用类(Exception的子类)或实例参数调用raise语句引发异常。使用类时程序会自动创建实例。

例如:

>>> raise ExceptionTraceback (most recent call last): File "<stdin>", line 1, in <module>Exception>>> raise NameError('This is NameError')Traceback (most recent call last): File "<stdin>", line 1, in <module>NameError: This is NameError>>>

由操作结果看到,第一个示例raise Exception引发了一个没有相关错误信息的普通异常,第二个示例输出了一些错误提示。

如果只想知道是否抛出了异常,并不想处理,使用一个简单的raise语句就可以再次把异常抛出,例如:

try: raise NameError('This is NameError')except NameError: print('An exception happened') # raise 不加reise 输出对应字符结束程序 try: raise NameError('This is NameError') # 当前语句中的信息被输出except NameError: print('An exception happened') # 添加raise则打印对应字符并再次显示异常 raise

由输出结果看到,使用raise可以输出更深层次的异常。在使用过程中,可以借助该方法得到更详尽的异常信息。

异常中的else

如果程序执行完异常还需要做其他事情,怎么办呢?

异常为我们提供了try…except…else语句实现该功能,语法如下:

try: <语句>  # 运行别的代码except <名字>: <语句>  # 如果在try部分引发了异常else: <语句>  # 如果没有发生异常

如果在try子句执行时没有发生异常,就会执行else语句后的语句(如果有else)。使用else子句比把所有语句都放在try子句里面更好,这样可以避免一些意想不到而except又没有捕获的异常。

例如:

def model_exception(x, y): try: a = x / y except: print('程序出现异常...') else: print('程序无异常则执行此语句...')model_exception(2, 1)

执行结果如下:

程序无异常则执行此语句...

由执行结果看到,没有发生异常时,会执行else子句的流程。

综上所述,当程序没有发生异常时,通过添加一个else子句做一些事情(比如输出一些信息)很有用,可以帮助我们更好地判断程序的执行情况。

自定义异常

尽管内建异常类包括大部分异常,而且可满足很多要求,但有时还是要创建自己的异常类。比如需要精确知道问题的根源,就需要使用自定义异常精确定位问题。可以通过创建一个新exception类拥有自己的异常。异常应该继承自Exception类,可以直接继承,也可以间接继承。

因为错误就是类,捕获一个错误就是捕获该类的一个实例,因此错误并不是凭空产生的,而是由一些不合理的部分导致的。Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。如果要抛出错误,那么可以根据需要定义一个错误的类,选择好继承关系,然后用raise语句抛出一个错误的实例。

例如(my_error.py):

class MyError(Exception): def __init__(self): pass def __str__(self): return 'this is self define error'def my_error_test(): try: raise MyError() except MyError as e: print('exception info: ', e)my_error_test()

执行结果如下:

exception info: this is self define error

由程序和执行结果看到,程序正确执行了自定义的异常,并且需要继承Exception类。

这只是一个简单的示例,还有不少细节需要琢磨,此处不做深入探讨,有兴趣的同学可以查阅相关资料进行实践。

提示:异常最好以Error结尾,一方面贴近标准异常的命名,另一方面便于见名知意。

finally 子句

Python中的finally子句需要和try子句一起使用,组成try / finally的语句形式,try / finally语句无论发生异常与否都将执行最后的代码。

例如(use_finally.py):

def use_finally(x, y): try: a = x / y finally: print('No matter what happened, I will show in front of you')use_finally(2, 0)

执行结果为:

Traceback (most recent call last): File "/Users/poppies/Desktop/python_projects/基础部分/use_finally.py", line 8, in <module> use_finally(2, 0) File "/Users/poppies/Desktop/python_projects/基础部分/use_finally.py", line 3, in use_finally a = x / yZeroDivisionError: division by zeroNo matter what happened, I will show in front of you

由执行结果看到,finally子句被执行了,无论try子句中是否发生异常,finally都会被执行。

这里我们有一个疑问,虽然执行了finally子句,但是最后还是抛出异常了,是否可以使用except截获异常呢?

可以使用except截获异常。try、except、else和finally可以组合使用,但要记得else在except之后,finally在except和else之后。

对于上面的示例,可以更改如下(use_finally_1.py):

def use_finally(x, y): try: a = x / y except ZeroDivisionError: print('Some bad thing happened: division by zero') finally: print('No matter what happened, I will show in front of you')use_finally(2, 0)

执行结果如下:

Some bad thing happened: division by zeroNo matter what happened, I will show in front of you

由执行结果看到,先执行了except子句的输出语句,后面跟着执行了finally子句的输出语句。如果再添加else子句,当程序正常运行时会先执行else子句,然后执行finally子句。在有finally的异常处理程序中,finally中的子句一定是最后执行的。finally子句在关闭文件或数据库连接时非常有用(文件操作和数据库操作后面会具体讲解)。

提示:在Python 2.5之前的版本中,finally需要独立使用,不能与try语句配合。在Python 2.5之后才支持这些语句的组合使用。

0 阅读:0

李光朱课程

简介:感谢大家的关注