异常处理


异常

异常是程序发生错误的信号,程序一旦出错就会产生异常,异常是一个对象,若没有捕获异常并处理,则会抛出异常,程序的运行就会终止。


异常的三部分

  • 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

原文地址:https://www.cnblogs.com/ChiRou/p/14277883.html