前言
在编写程序的过程中,程序员通常希望识别正常执行的代码和执行异常的代码。这种异常可能程序的错误,也可以是希望不要发生的事情。为了能够处理这些异常,可以在所有可能发生这种情况的地方使用条件语句进行判断。但是这么做既效率低又不灵活,而且还无法保证条件语句覆盖了所有可能的异常。为了更好的解决这个问题,Python语言提供了非常强大的异常处理机制。通过这种机制,可以直接处理所有发生的异常,也可以选择忽略这些异常。
一、什么是异常?
Python语言用异常对象(exception object)来表示异常情况。当遇到错误后,会引发异常。如果异常对象没有处理异常,或未捕捉异常,程序就会终止执行,并向用户返回异常信息。通常异常信息会告知出现会告知出现错误的代码行以及有助于定位错误的信息,以便程序员可以快速定位有错误的代码行。
让程序抛出异常的情况很多,但可以分为两大类:系统自己抛出的异常和主动抛出的异常。如果执行了某些代码(如分母为0的除法),系统会抛出异常,这种异常属于系统自动抛出的异常(由Python解释器抛出)。还有一种异常,是由于执行raise语句抛出的异常,这种异常属于主动抛出来的异常。这样做的目的是主要是主要是系统多种异常都有统一的代码处理,所以将程序从当前的代码行之间跳到异常处的代码块。
x=1/0 #抛出异常
由于分母是0,所以执行上面的代码,会在Console中输出下面的异常信息。
Traceback (most recent call last):
File "D:\ProgramData\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2963, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-2-6b33bf84d682>", line 1, in <module>
x=1/0
ZeroDivisionError: division by zero
很明显,异常信息明确明确指出异常在第一行,并将抛出异常的代码显示在了异常信息中。
在捕获异常的时候,可以选择用同一个代码块儿处理所有的异常,也可以每一个异常有一个代码块儿处理。之所以可以单独对某个异常进行处理,是因为每一个异常就是一个类,抛出异常的过程也就是创建这些类的实例的过程。如果单独捕获某个异常类的实例,那么自然可以用某个代码块儿单独处理带异常。
二、主动抛出异常
异常可以是系统自动抛出的,也可以由程序员编写代码来主动抛出。本节将介绍如何通过raise语句自己来抛出异常以及如何定义异常类。
1、raise语句
使用raise语句可以会直接抛出异常类。Raise语句可以使用一个类(必须是exception类或者exception类的子类)或异常对象抛出异常。如果使用类异常,会自动创建类的实例,下面的一些代码会使用内建的exception异常类抛出异常
raise Exception
输出结果:
Traceback (most recent call last):
File "D:\ProgramData\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2963, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-3-2aee0157c87b>", line 1, in <module>
raise Exception
Exception
上面的代码在raise语句后跟了一个Exception类,执行这行代码会抛出上述所示的异常信息。从这个异常信息可以看出,除了抛出异常信息的代码文件和代码行外,没有其他有价值的信息。如果程序抛出的异常都是这些信息,那么就无从得知到底是什么原因引发的异常。因此,最简单的做法就是为异常信息加上一个描述。
raise Exception('这是自己主动抛出的异常')
上面的代码在raise语句中加了一个Exception对象,并通过类的构造方法传入了异常信息的描述。执行这行代码,会抛出如下所示的异常信息。
输出结果:
Traceback (most recent call last):
File "D:\ProgramData\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2963, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-4-508240aac885>", line 1, in <module>
raise Exception('这是自己主动抛出的异常')
Exception: 这是自己主动抛出的异常
很明显,上述所示的异常信息的最后显示了添加的异常信息。描述这样的议程更容易让人理解。
在python语句中内置了很多异常类用来描述特定类型的异常。如ArithmeticError表示与数值有关的异常.
raise AttributeError('这是一个数值有关的异常')
执行这行艾玛会抛出如下信息:
输出结果:
Traceback (most recent call last):
File "D:\ProgramData\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2963, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-5-0fa9405a6566>", line 1, in <module>
raise AttributeError('这是一个数值有关的异常')
AttributeError: 这是一个数值有关的异常
尽管AttributeError类没有强迫我们必须用它来表示数值有关的异常,但使用有意义的异常类是一个好习惯,就像为变量命名一样,尽管可以命名为a,b,c,但为每一个变量起一个有意义的名字,会让程序的可读性大大加强。
使用内建的异常类是不需要导入任何模块儿的,不过要使用其他模块儿中的异常类,就需要导入相应的模块。下面的异常抛出了InvalidRoleException异常,该类通常表示与Role相关的异常()至于什么是Rose,后面会解释)InvalidRoleException类的构造方法有两个参数,第一个参数需要传入一个数值,表示状态;第二个参数需要传入一个字符串,表示抛出异常的原因。
from boto.codedeploy.exceptions import InvalidRoleException
raise InvalidRoleException(2,'这是一个与Role有关的异常')
运行结果如下:
输出结果:
Traceback (most recent call last):
File "D:\ProgramData\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2963, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-7-d1471c808e7e>", line 2, in <module>
raise InvalidRoleException(2,'这是一个与Role有关的异常')
boto.codedeploy.exceptions.InvalidRoleException: InvalidRoleException: 2 这是一个与Role有关的异常
下表中描述了一些最重要的内建异常类。
异常类名 | 描述 |
---|---|
Exception | 所有异常的基类 |
AttributeError | 属性引用或赋值失败时抛出的异常 |
OSError | 当操作系统无法执行任务时抛出的异常 |
IndexError | 在使用序列中不存在的索引时抛出的异常 |
KeyError | 在使用影射中不存在的键值时抛出的异常 |
NameError | 在找不到名字(变量)时抛出的异常 |
SyntaxError | 在代码为错误形式时抛出触发 |
TypeError | 在内建操作或函数应用于错误类型的对象时抛出的异常 |
ValueError | 在内建操作或者函数应用于正确类型的对象,但该对象使用了不合规的值时抛出的异常 |
ZeroDivsionError | 在除法或取模操作的第二个参数值为0时抛出的异常 |
2、自定义异常类
在很多时候需要自定义异常类,任何一个异常类必须是Exception的子类。最简单的自定义异常类就是一个空的Exception类的子类。
class myException(Exception):
pass
下面用一个科幻点的例子来演示如何自定义异常类,以及如何抛出自定义异常。
本例将会定义一个曲速引擎过载的异常类
class WarpdriveOverloadExcepthon(Exception):
pass
#当前曲速值
warpSpeed=12
#当曲速值为10或以上值时认为是曲速引擎过载,应该抛出异常
if warpSpeed>=10:
#抛出自定义异常
raise WarpdriveOverloadExcepthon('曲速引擎已经过载,请停止或弹出曲速引擎,否则飞船将会爆炸')
运行结果如下:
Traceback (most recent call last):
File "D:\ProgramData\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2963, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-8-2b4958b8c957>", line 8, in <module>
raise WarpdriveOverloadExcepthon('曲速引擎已经过载,请停止或弹出曲速引擎,否则飞船将会爆炸')
WarpdriveOverloadExcepthon: 曲速引擎已经过载,请停止或弹出曲速引擎,否则飞船将会爆炸
其实在自定义异常类中,可以做更多的工作,如为异常类的构造方法添加更多的参数。但到目前为止,关于python类的更高级应用还没有涉及,所以本例只是实现了一个最简单的自定义成类,关于python类更高级的内容会在后续有更详细的讲解,届时可以使用Python类的高级奇数编写更复杂的异常类。
总结
主要介绍什么是异常,以及如何抛出异常、自定义异常。后续将介绍如何捕获异常,原计划是整理大厂的一些机试题,但是如果参加过大厂的机试题的话,你会发现,代码中都会有 try……except,,,这个其实就是下一节将要涉及的如何捕获异常,所以学习本节还是很有必要的。