异常处理
异常
异常是程序发生错误的信号,程序一旦出错就会产生异常,异常是一个对象,若没有捕获异常并处理,则会抛出异常,程序的运行就会终止。
异常的三部分
- 1、异常的追踪信息,显示哪个文件,第几行出现错误。
- 2、异常的类型,类型即类,一种类型标识一种错误。
- 3、异常的内容,具体的错误信息。
Traceback (most recent call last): 异常的追踪信息
File "D:/Desktop/异常处理.py", line 67, in <module>
xxx
NameError: name 'xxx' is not defined
异常类型 异常值
异常类型
不同的异常可以用不同的类型去标识,一个异常标识一种错误。常见的类型有:
类型 | 含义 |
---|---|
BaseException | 所有内置异常的基类 |
Exception | 所有内置的非系统退出类的异常都派生自此类 |
AttributeError | 访问一个对象不存在的属性 |
IOError | 输入/输出异常,基本上是无法打开文件 |
ImportError | 无法导入模块或包,基本上是路径问题或名称错误 |
IndentationError | 语法错误(的子类);代码没有正确对齐 |
IndexError | 索引超出序列边界 |
KeyError | 访问字典不存在的key |
KeyboardInterrupt | Ctrl+c被按下 |
NameError | 调用未定义的变量 |
SyntaxError | 非法代码,语法错误 |
TypeError | 传入的对象类型与要求不符合 |
UNboundLocalError | 访问一个未定义的局部变量,通常是由于有一个同名的全局变量。 |
ValueError | 传入一个调用者不期望的值,即使值的类型是正确的。 |
处理异常
为了增强程序的健壮性,即便是程序运行过程中出错了,也不要终止程序,而是将异常捕捉并处理,将出错信息记录到日志内。
错误有两大来源:
- 一、语法错误SyntaxError:必须在程序运行前就改正。
- 二、逻辑错误。
- 1、可预知错误:应在逻辑设计上就处理,通常用if判断。
- 2、不可预知错误:使用异常处理语句。
异常处理语句
单异常
try:
子代码块
except 异常类型:
捕捉到异常后执行的代码.
# 多异常都使用相同的处理方式
try:
子代码块
except (异常类型1,异常类型2...):
捕捉到异常后执行的代码.
# 万能异常
try:
子代码块
except Exception as e: # Exception任意异常都能捕获到.
所有的异常都会触发处理
# 无法预知异常
try:
子代码块
finally:
无论被监测的子代码块有无异常发生,都会执行finally的子代码块。
多异常
try:
子代码块
except 异常类型1 as e: # as会将异常信息赋值给后面的变量名.
捕捉到异常类型1会执行的代码.
except 异常类型2:
捕捉到异常类型2会执行的代码.
...
多异常使用相同处理方式
try:
子代码块
except (异常类型1,异常类型2...) as e:
捕捉到其中任一异常后执行的代码.
except 异常类型3:
pass
...
任意异常
通常用于某些无法预知异常类型的时候。
try:
子代码块
except Exception: # Exception能匹配任意异常
任意异常都会触发执行.
没有异常
try:
子代码块
except 异常类型:
pass
else:
没有异常则会触发执行
无论是否有异常
try:
子代码块
finally:
无论是否有异常都会执行.
finally不会处理异常,出现异常会先执行finally内的代码,然后终止子代码块执行。通常应该将被监测代码中回收系统资源的部分放到这里。比如关闭文件,关闭数据库连接等。
完整语法
try:
# 有可能会抛出异常的子代码块,任何一行抛异常,其下的代码都不会执行。
子代码1
子代码2
...
except 异常类型1 as e:
捕捉到异常1后执行的代码.
except 异常类型2 as e: # as表示将异常信息赋值给后面的变量.
捕捉到异常2后执行的代码.
...可以有多个except...
else:
若被监测的子代码块没有异常发生,则会执行else的子代码块。
finally:
无论被监测的子代码块有无异常发生,都会执行finally的子代码块。
else不能单独与try配合使用;else可以不用,如果用必须在except后面使用。
实例:
try:
f = open('a.txt','rb')
f.write()
except BaseException as e:
print(e.args) # ('write',)
finally:
f.close()
以r模式打开文件不能向文件写入内容,所以会抛异常,e会接收到异常的内容。
异常是附加给程序的一种异常处理的逻辑,与主要程序的工作是没有关系的,过多的异常处理语句会导致代码可读性变差,只有在错误发生的条件无法预知的情况下,才应该使用异常处理。
主动抛异常
不符合解释器的语法或逻辑规则时,解释器会自动抛异常,而对于违反开发者定义的各类规则时,则需要开发者自己主动抛出异常,主动抛异常使用的是raise
关键字,raise
后面必须是一个异常的类或者是异常的实例。
def total(x,y):
if not isinstance(x,int):
raise TypeError('x must be int')
elif not isinstance(y,int):
raise TypeError('y must be int')
return x + y
total(1.1,2) # TypeError: x must be int
total(1,2.2) # TypeError: y must be int
自定义异常类型
内置异常不符合期望时,可以通过继承内置的异常类来自定义异常类。自定义异常类必须继承内置异常类,通常是Exception
类。
class CommonError(Exception):
# 初始化异常信息
def __init__(self,msg='异常信息'):
self.msg = msg
# 定义触发异常类型时打印的异常信息格式
def __str__(self):
return f' {self.msg} '
raise CommonError # __main__.CommonError: 异常信息
raise CommonError('自定义的异常信息') # __main__.CommonError: 自定义的异常信息
也可以在特定异常的基础上扩展一个相关的异常.
class CommonError(IOError):
pass
raise CommonError # __main__.CommonError
断言
断言语句assert expression
,断定表达式expression成立,否则触发异常AssertionError
,与raise-if-not
的语义相同.
assert isinstance(10,int)
assert 10 < 9 # AssertionError
等同于
if not isinstance(10,int):
raise AssertionError
if not 10 < 9:
raise AssertionError
断言语句通常用于程序调试过程中使用,实际生产环境中不应使用断言。
参考文档:
https://docs.python.org/zh-cn/3/library/exceptions.html?highlight=exception#Exception