Python基础学习15--异常的分类与处理

时间:2021-12-22 20:02:51

异常

  即便Python程序的语法是正确的,在运行它的时候,也有可能发生错误

  在python中,异常是一个类,可以处理和使用

异常的分类

  BaseException是所有内置异常的基类,但用户定义的类并不直接继承BaseException,所有的异常类都是从Exception继承,且都在exceptions模块中定义。Python自动将所有异常名称放在内建命名空间中,所以程序不必导入exceptions模块即可使用异常。一旦引发而且没有捕捉SystemExit异常,程序执行就会终止。如果交互式会话遇到一个未被捕捉的SystemExit异常,会话就会终止。

  内置异常类的层次结构摘自:https://blog.csdn.net/polyhedronx/article/details/81589196

BaseException       # 所有异常的基类
 +-- SystemExit             # 解释器请求退出
 +-- KeyboardInterrupt      # 用户中断执行(通常是输入^C)
 +-- GeneratorExit          # 生成器(generator)发生异常来通知退出
 +-- Exception              # 常规异常的基类
      +-- StopIteration             # 迭代器没有更多的值
      +-- StopAsyncIteration        # 必须通过异步迭代器对象的__anext__()方法引发以停止迭代
      +-- ArithmeticError           # 各种算术错误引发的内置异常的基类
      |    +-- FloatingPointError           # 浮点计算错误
      |    +-- OverflowError                # 数值运算结果太大无法表示
      |    +-- ZeroDivisionError            # 除(或取模)零 (所有数据类型)
      +-- AssertionError            # 当assert语句失败时引发
      +-- AttributeError            # 属性引用或赋值失败
      +-- BufferError               # 无法执行与缓冲区相关的操作时引发
      +-- EOFError                  # 当input()函数在没有读取任何数据的情况下达到文件结束条件(EOF)时引发
      +-- ImportError               # 导入模块/对象失败
      |    +-- ModuleNotFoundError          # 无法找到模块或在在sys.modules中找到None
      +-- LookupError               # 映射或序列上使用的键或索引无效时引发的异常的基类
      |    +-- IndexError                   # 序列中没有此索引(index)
      |    +-- KeyError                     # 映射中没有这个键
      +-- MemoryError               # 内存溢出错误(对于Python 解释器不是致命的)
      +-- NameError                 # 未声明/初始化对象 (没有属性)
      |    +-- UnboundLocalError            # 访问未初始化的本地变量
      +-- OSError                   # 操作系统错误,EnvironmentError,IOError,WindowsError,socket.error,select.error和mmap.error已合并到OSError中,构造函数可能返回子类
      |    +-- BlockingIOError              # 操作将阻塞对象(e.g. socket)设置为非阻塞操作
      |    +-- ChildProcessError            # 在子进程上的操作失败
      |    +-- ConnectionError              # 与连接相关的异常的基类
      |    |    +-- BrokenPipeError                 # 另一端关闭时尝试写入管道或试图在已关闭写入的套接字上写入
      |    |    +-- ConnectionAbortedError          # 连接尝试被对等方中止
      |    |    +-- ConnectionRefusedError          # 连接尝试被对等方拒绝
      |    |    +-- ConnectionResetError            # 连接由对等方重置
      |    +-- FileExistsError              # 创建已存在的文件或目录
      |    +-- FileNotFoundError            # 请求不存在的文件或目录
      |    +-- InterruptedError             # 系统调用被输入信号中断
      |    +-- IsADirectoryError            # 在目录上请求文件操作(例如 os.remove())
      |    +-- NotADirectoryError           # 在不是目录的事物上请求目录操作(例如 os.listdir())
      |    +-- PermissionError              # 尝试在没有足够访问权限的情况下运行操作
      |    +-- ProcessLookupError           # 给定进程不存在
      |    +-- TimeoutError                 # 系统函数在系统级别超时
      +-- ReferenceError            # weakref.proxy()函数创建的弱引用试图访问已经垃圾回收了的对象
      +-- RuntimeError              # 在检测到不属于任何其他类别的错误时触发
      |    +-- NotImplementedError          # 在用户定义的基类中,抽象方法要求派生类重写该方法或者正在开发的类指示仍然需要添加实际实现
      |    +-- RecursionError               # 解释器检测到超出最大递归深度
      +-- SyntaxError               # Python 语法错误
      |    +-- IndentationError             # 缩进错误
      |         +-- TabError                        # Tab和空格混用
      +-- SystemError               # 解释器发现内部错误
      +-- TypeError                 # 操作或函数应用于不适当类型的对象
      +-- ValueError                # 操作或函数接收到具有正确类型但值不合适的参数
      |    +-- UnicodeError                 # 发生与Unicode相关的编码或解码错误
      |         +-- UnicodeDecodeError              # Unicode解码错误
      |         +-- UnicodeEncodeError              # Unicode编码错误
      |         +-- UnicodeTranslateError           # Unicode转码错误
      +-- Warning                   # 警告的基类
           +-- DeprecationWarning           # 有关已弃用功能的警告的基类
           +-- PendingDeprecationWarning    # 有关不推荐使用功能的警告的基类
           +-- RuntimeWarning               # 有关可疑的运行时行为的警告的基类
           +-- SyntaxWarning                # 关于可疑语法警告的基类
           +-- UserWarning                  # 用户代码生成警告的基类
           +-- FutureWarning                # 有关已弃用功能的警告的基类
           +-- ImportWarning                # 关于模块导入时可能出错的警告的基类
           +-- UnicodeWarning               # 与Unicode相关的警告的基类
           +-- BytesWarning                 # 与bytes和bytearray相关的警告的基类
           +-- ResourceWarning              # 与资源使用相关的警告的基类。被默认警告过滤器忽略

代码实例如下:

num = int(input('Please enter a number : '))      # 请输入一个数字
res = 100/num    # 计算规则
print('100 ÷ {0} = {1}'.format(num, res))         # 输出计算结果

print('I want to sleep !')                        # 打印“I want to sleep !”
# 1、你永远不知道别人会输入什么!
# 2、不管别人输入什么,我都希望能够输出 I want to sleep !
# 输入:3 100 ÷ 3 = 33.333333333333336 I want to sleep ! # 成功输出:I want to sleep ! # 输入:3.3 num = int(input('Please enter a number : ')) # 会在这一句报错:3.3无法转换为int类型 ValueError: invalid literal for int() with base 10: '3.3' # 输入:abc num = int(input('Please enter a number : ')) # 同样,abc无法转换为int类型 ValueError: invalid literal for int() with base 10: 'abc' # 输入:0 res = 100/num # 会在该行报错,分母不能为 0 ZeroDivisionError: division by zero # 如果想要输入:I want to sleep ! 就不能让程序出现异常报错;但是怎样对异常进行处理呢?

异常处理

  • 我们无法保证程序永远正确运行
  • 但是,必须保证程序在最坏的情况下得到的问题被妥善处理-----我管怎样,我就是想要输出:I want to sleep !
  • python的异常处理模块语法如下    
try:
    尝试实现某个操作
    如果没出现异常,任务就可以完成
    如果出现异常,将异常从当前代码块扔出去尝试解决异常

except 异常类型1:
    解决方案1:用于尝试在此处处理异常解决问题

except 异常类型2:
    解决方案2:用于尝试在此处处理异常解决问题

except (异常类型3,异常类型4...):
    解决方案:针对多个异常使用相同的处理方式

excpet:
    解决方案:所有异常的解决方案

else:
    如果没有出现任何异常,将会执行此处代码

finally:
    管你有没有异常都要执行的代码
  • 流程:
    1. 执行 try 下面的语句块
    2. 如果出现异常,则在 except 语句中查找对应异常并进行处理
    3. 如果没有出现异常,则执行 else 语句内容
    4. 最后,不管是否出现异常,都要执行 finally 语句
    5. 除 except (最少一个)以外,else 和 finally 可缺省

代码实例如下:

try:
    num = int(input('Please enter a number : '))      # 请输入一个数字
    res = 100/num    # 计算规则
    print('100 ÷ {0} = {1}'.format(num, res))         # 输出计算结果
except:                                               # 不管try中有任何异常,都执行except语句
    pass
finally:                                              # 不管有无异常,都执行finally语句
    print('I want to sleep !')                        # 打印“I want to sleep !”

输出结果如下:

# 如果try中存在异常,输出如下
# Please enter a number : 1.1
# Please enter a number : ABC
# Please enter a number : 0
I want to sleep !

# 如果try中无异常报错,输出如下:
# Please enter a number : 20
100 ÷ 20 = 5.0
I want to sleep !

通过以上代码我们可以看到,我们已经初步实现输出 I want to sleep !

但是,我们只是捕获了所有异常,并没有告诉用户,哪里输入有问题,因此我们最好可以捕获到单个异常并做处理

代码实例如下: 

try:
    num = int(input('Please enter a number : '))      # 请输入一个数字
    res = 100/num    # 计算规则
    print('100 ÷ {0} = {1}'.format(num, res))         # 输出计算结果

# 在处理异常的时候,一旦捕获到某一异常,则不再执行后面的except语句
except ValueError as v:                               # 如果报错为ValueError,则执行该语句
    print('ValueError:',v)
except ZeroDivisionError as z:                        # 如果报错为ZeroDivisionError,则执行该语句
    print('ZeroDivisionError:',z)
# 所有异常均继承自 Exception
# except后不写或者写 Exception ,任何异常都会被捕获
# 一般这种写法都会放在最后,以便之前的except可以准确的捕获异常
except Exception as e:                                # 如果出现非 ValueError, ZeroDivisionError 的报错,则执行该语句
    print('我也不知道什么异常,反正有问题;报错原因:',e)

finally:                                              # 不管有无异常,都执行finally语句
    print('I want to sleep !')                        # 打印“I want to sleep !”

 输出结果如下:

# Please enter a number : 3.3
ValueError: invalid literal for int() with base 10: '3.3'
I want to sleep !

# Please enter a number : abc
ValueError: invalid literal for int() with base 10: 'abc'
I want to sleep !

# Please enter a number : 0
ZeroDivisionError: division by zero
I want to sleep !

# Please enter a number : 33
100 ÷ 33 = 3.0303030303030303
I want to sleep !

主动抛出异常:

  • 在某些情况下,我们希望自己去主动抛出异常的时候,则可以使用 raise 关键字来实现
  • raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。

代码实例如下:

num = int(input('Please enter a number : '))      # 请输入一个数字
if num < 0:                                       # 我们在这里定义了,如果分母小于0,给我抛错 ValueError
    raise ValueError                              # raise 的后面必须跟 Exception类的子类
res = 100/num    # 计算规则
print('100 ÷ {0} = {1}'.format(num, res))         # 输出计算结果

输出结果如下:

# 当我们输入 20 的时候,可以正常输出
100 ÷ 20 = 5.0

# 当我们输入 -20 的时候,应会抛出 ValueError
ValueError

自定义异常

  • 如果需要我们使用 raise 抛出异常,则建议自定义异常
  • 在自定义异常的时候,一般包含以下内容:
    • 自定义发生的异常代码
    • 自定义发生异常后的问题提示
    • 自定义发生异常的行数
  • 我们想要实现的是,一旦出现异常,方便我们快速定位错误

代码实例如下: 

class MyError(ValueError):                      #我们创建一个新的报错,继承自ValueError
    pass

num = int(input('Please enter a number : '))    # 请输入一个数字
if num < 0:                                     # 我们在这里定义了,如果分母小于0,给我抛出自定义的 MyError
    raise MyError                               # raise 的后面必须跟 Exception类的子类:Exception --> ValueError --> MyError
res = 100/num    # 计算规则
print('100 ÷ {0} = {1}'.format(num, res))       # 输出计算结果

 输出结果如下: 

# 当我们输入 -20 的时候,应会抛出 MyError
__main__.MyError