简介
异常是中断代码块的正常控制流程以便处理错误或其他异常条件的一种方式;异常会在错误被检测到的位置引发;python解释器会在检测到运行时错误的时候引发异常。当然,也可以通过raise语句显示的引发异常;异常可以通过try...except捕获;当一个异常完全未被处理时,解释器会终止程序的执行,或返回交互模式的主循环。无论哪种情况,都会打印栈回溯信息,除非当异常为SystemExit的时候;
异常处理
我们可以通过try...except来进行异常捕获;try语句可以有多个except子句来为不同异常指定处理程序。但最多只有一个处理程序会被执行;处理程序只处理对应的try子句中发生的异常;
格式:
try:
...
(except [expression [as name]]: ...)+
[else: ...]
[finally: ...]
异常处理原理
- 首先,执行try子句
- 如果没有触发异常,则跳过except子句
- 如果定义了finally,则执行finally子句
- 如果执行try子句时发生异常,则跳过该子句中剩余部分代码;且与except子句进行匹配并执行except子句
- 如果发生的异常与except子句中指定的异常不匹配,则它会传递到外部的try语句中;如果没有找到处理程序,则它是一个未处理异常且执行将终止并输出异常信息;
except
except子句指定一个或多个异常处理程序。仅在try子句中发生异常时才会被执行;当try子句发生异常时,将启动对异常处理程序的搜索。会逐一检查except子句直至找到与该异常匹配的子句;
- 如果存在无表达式的except子句,它必须放置在最后一个,因为它匹配任何异常;
- 如果发生的异常与except子句中的类是同一个类或是它的基类时,则该类与该异常相兼容
- 如果未匹配,则异常会被传播给发起调用栈,除非存在finally子句正好引发了另一个异常。新异常将导致旧异常丢失
- 如果except子句引发异常,则原来的搜索会被取消,并在调用栈上自动启动对新异常的搜索;
- 如果匹配到except子句时,该异常将被赋值给except子句在as关键字之后指定的目标,如果存在此关键字的话;并且except子句将被执行;except子句结束时被清除
在except子句被执行前,有关异常详细信息存放在sys模块中,可通过sys.exc_info()访问。sys.exc_info()返回一个由异常类、异常实例和回溯对象组成的元组,用于程序中标识异常发生点。当从处理异常的代码返回时,通过sys.exc_info()访问的异常信息会恢复到之前的值;
如:
def test():
raise Exception('ww')
try:
test()
except:
print(sys.exc_info())
#(<class 'Exception'>, Exception('ww'), <traceback object at 0x7fbe5e026040>)
如果控制流离开try子句时没有触发异常,并且没有return、continue、break语句,可选的else子句将被执行
标准异常
异常名称 | 释义 |
BaseException | 所有异常的基类 |
Exception | 常规错误的基类 |
AssertionError | assert失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入即达到文件结束条件 |
ImportError | 导入失败 |
ModuleNotFoundError | 未找到导入的模块 |
IndexError | 索引超出范围 |
KeyError | 映射中不存在键 |
NameError | 局部或全局名称未找到 |
SyntaxError | 解析器遇到语法错误 |
SystemError | 解析器内部出现异常 |
TypeError | 应用于类型不适当 |
ZeroDivisionError | 分母为0 |
PermissionError | 无权限 |
TimeoutError | 超时 |
Warning | 警告基类 |
UserWarning | 用户代码所产生警告的基类 |
SyntaxWarning | 与模糊的语法相关的警告的基类 |
RuntimeWarning | 与模糊允许时行为相关的警告的基类 |
ImportWarning | 导入时的警告 |
finally
如果存在finally,则finally子句是try语句结束前执行的最后一项任务。不论try是否触发异常,都会执行finally子句。
- 如果子句中存在未处理异常,该异常会被临时保存,finally子句执行后被重新触发
- 如果finally子句发生异常,则异常被设置为新异常的上下文
- 如果finally执行了return、continue、break语句,则保存的异常会被丢弃
- 如果执行try子句时执行了return、continue、break语句,finally也会被执行。函数返回值由最后被执行的return决定,由于finally子句总是被执行,因此finally子句中被执行return语句重视最后被执行的;
def test1():
try:
1/0
finally:
return 40
test1()
#40
def test2():
try:
return "try"
finally:
return "finally"
test2()
# 'finally'
触发异常
在python中,raise支持强制触发异常;唯一的参数就是要触发的异常;这个参数必须是异常实例或异常类;如果是异常类,将通过调用没有参数的构造函数来隐式实例化;
格式:
raise [expression [from expression]]
- from:用于异常链;如果有该子句,第二个表达式必须为另一个异常类或实例;表明一个异常是由另一个异常直接的结果导致的;
只触发异常,不处理异常:
>>> raise
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: No active exception to reraise
传递异常类:
>>> raise ValueError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError
raise...from:
>>> def func():
... raise ConnectionError
...
>>> try:
... func()
... except ConnectionError as exc:
... raise RuntimeError('failed to open database') from exc
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in fund
ConnectionError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: failed to open database
如果不想要自动关联异常上下文机制,可以通过raise...from None来进行关闭:
>>> try:
... print(1/0)
... except:
... raise RuntimeError('something bad happened')
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: something bad happened
# raise ... from None
>>> try:
... print(1/0)
... except:
... raise RuntimeError('something bad happened') from None
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: something bad happened
>>>
自定义异常类
我们可以创建新的异常类,由于所有内置的非系统退出类异常都派生自此类,所以我们自定义的异常也应当从Exception派生;
简单的自定义异常:
class TError(Exception):
pass
使用:
>>> raise TError('自定义')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.TError: 自定义
自定义模版:
class TestError(Exception):
def __init__(self, msg):
super().__init__(self)
self.message = msg
def __str__(self):
return self.message
使用:
>>> raise TestError('测试自定义异常')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.TestError: 测试自定义异常