异常 Exception

  • 错误 Error
    逻辑设计错误、笔误、语法错误,即在运行前可以避免的

  • 异常Exception
    程序执行时的意外情况,前提是没有上面的错误情况,但在某些情况下出现的意外导致程序无法正常执行。
    例如open函数操作一个文件,但文件不存在,或网络访问一个文件,突然断网了,就是异常。

异常可以捕获,但错误是不能捕获的

产生异常

  • Pyhon解释器检测到异常并引发
  1. def foo():
  2. print('before')
  3. def bar():
  4. print(1/0) # 除0异常
  5. bar()
  6. print('after')
  7. foo() # 运行时会直接抛出
  • raise语句显示抛出异常
  1. def bar():
  2. print('before')
  3. raise Exception('my exception') # 显示抛出异常
  4. print('after') # raise后的语句不在执行
  5. bar()

程序会在异常抛出的地方中断执行,如果不捕获,就会提前结束程序

raise语句如果后面什么都没有,表示抛出最近一个被激活的异常,如果没有被激活的异常,则抛出类型异常,一般很少用。

raise后要求应该是BaseException类的子类或实例,如果是类,将被无参实例化。

异常的捕获

  1. # 语法
  2. try:
  3. 待捕获异常的代码块
  4. except [异常类型]:
  5. 异常处理的代码块

示例:

  1. def bar():
  2. try:
  3. print('begin')
  4. c = 1/0
  5. print('end')
  6. except:
  7. print('catch the exception')
  8. bar()
  9. # 执行到1/0异常时,由于使用了try...except捕获到异常,则异常生成位置之后语句将不再执行,转而执行对应的except部分语句
  10. begin
  11. catch the exception

捕获指定类型的异常

  1. try:
  2. print('begin')
  3. c = 1/0
  4. print('end')
  5. except ArithmeticError: # 数值计算类型异常,可以正常捕获
  6. print('catch the exception')
  7. try:
  8. print('begin')
  9. c = 1/0
  10. print('end')
  11. except KeyboardInterrupt: # 用户中断执行(通常是输入^C),1/0异常不属于这种类型,则不会被捕获
  12. print('catch the exception')

异常类的继承层次

  1. BaseException #所有异常的基类
  2. +-- SystemExit # 解释器请求退出
  3. +-- KeyboardInterrupt # 用户中断执行(通常是输入^C)
  4. +-- GeneratorExit # 生成器(generator)发生异常来通知退出
  5. +-- Exception # 常规错误的基类
  6. +-- StopIteration # 迭代器没有更多的值
  7. +-- StopAsyncIteration # 由异步迭代的`__anext__()`抛出的异常
  8. +-- ArithmeticError # 所有数值计算错误的基类
  9. | +-- FloatingPointError # 浮点计算错误
  10. | +-- OverflowError # 数值运算超出最大限制
  11. | +-- ZeroDivisionError # 除0异常
  12. +-- AssertionError # 断言语句失败
  13. +-- AttributeError # 对象没有这个属性
  14. +-- BufferError # 缓存错误
  15. +-- EOFError # 没有内建输入,到达EOF 标记
  16. +-- ImportError # 导入模块/对象失败
  17. | +-- ModuleNotFoundError # 模块不存在
  18. +-- LookupError # 无效数据查询的基类
  19. | +-- IndexError # 序列中没有此索引(index)
  20. | +-- KeyError # 映射中没有这个键
  21. +-- MemoryError # 内存溢出错误(对于Python 解释器不是致命的)
  22. +-- NameError # 未声明/初始化对象 (没有属性)
  23. | +-- UnboundLocalError # 访问未初始化的本地变量
  24. +-- OSError # 操作系统错误
  25. | +-- BlockingIOError # 调用阻塞异常错误
  26. | +-- ChildProcessError # 子进程异常
  27. | +-- ConnectionError # 连接异常基类
  28. | | +-- BrokenPipeError # 管道读写异常
  29. | | +-- ConnectionAbortedError # 连接失败
  30. | | +-- ConnectionRefusedError # 连接拒绝
  31. | | +-- ConnectionResetError # 连接重置
  32. | +-- FileExistsError # 文件已经存在异常
  33. | +-- FileNotFoundError # 文件未找到异常
  34. | +-- InterruptedError # 中断异常
  35. | +-- IsADirectoryError # 文件操作用在目录上
  36. | +-- NotADirectoryError # 不是目录异常
  37. | +-- PermissionError # 权限异常
  38. | +-- ProcessLookupError # 进程不存在异常
  39. | +-- TimeoutError # 超时异常
  40. +-- ReferenceError # 试图访问已经垃圾回收了的对象
  41. +-- RuntimeError # 一般的运行时错误
  42. | +-- NotImplementedError # 尚未实现的方法
  43. | +-- RecursionError # 超出最大递归深度
  44. +-- SyntaxError # Python 语法错误
  45. | +-- IndentationError # 缩进错误
  46. | +-- TabError # Tab 和空格混用
  47. +-- SystemError # 一般的解释器系统错误
  48. +-- TypeError # 对类型无效的操作
  49. +-- ValueError # 传入无效的参数
  50. | +-- UnicodeError # Unicode 相关的错误基类
  51. | +-- UnicodeDecodeError # Unicode 解码时的错误
  52. | +-- UnicodeEncodeError # Unicode 编码时错误
  53. | +-- UnicodeTranslateError # Unicode 转换时错误
  54. +-- Warning # 警告的基类
  55. +-- DeprecationWarning # 关于被弃用的特征的警告
  56. +-- PendingDeprecationWarning # 关于特性将会被废弃的警告
  57. +-- RuntimeWarning # 可疑的运行时行为(runtime behavior)的警告
  58. +-- SyntaxWarning # 可疑的语法的警告
  59. +-- UserWarning # 用户代码生成的警告
  60. +-- FutureWarning # 关于构造将来语义会有改变的警告
  61. +-- ImportWarning # 导入警告
  62. +-- UnicodeWarning # unicode相关警告
  63. +-- BytesWarning # 字节相关警告
  64. +-- ResourceWarning # 资源使用情况警告

示例:

import sys

try:
    sys.exit(1)
except SystemExit:   # 使用SystemExit捕获异常
    print('catch the exception')

print('outer')

多异常捕获

except可以捕获多个异常

try:
    a = 1/0
except ZeroDivisionError:
    print('1/0')
except Exception:
    print('Exception')

捕获规则:

捕获是从上到下依次比较,如果匹配,则执行匹配的except语句块

如果被一个except捕获,其他的except语句就不会再次捕获了

如果任何一个except都没有捕获到这个异常,则该异常直接抛出

捕获原则:

从小到大,从具体到宽泛

as子句

被抛出的异常,是异常的示例,可以使用as子句获得这个对象

try:
    a = 1/0
except ZeroDivisionError as e:  # 使用e获得对象
    print(e)
except Exception as e :
    print(e)

finally子句

在try…except语句块中,不管是否发生了异常,都会执行finally子句

f = None
try:
    f = open('text.txt')
except FileNotFoundError as e:
    print('{} {} {}'.format(e.__class__, e.errno, e.strerror))
finally:    # 无论如何都会执行f的关闭操作
    print('clear work')
    if f:
        f.close()

也可以在finally中再次捕捉异常

f = None
try:
    f = open('text.txt')
except FileNotFoundError as e:
    print('{} {} {}'.format(e.__class__, e.errno, e.strerror))
finally:
    print('clear work')
    try:
        f.close()
    except Exception as e:
        print(e)

异常的传递

异常总是向外层抛出,如果外层没有处理这个异常,就会继续向外抛出,如果内层捕获并处理了异常,外部就不能捕获到了,如果最外层还是没有处理,就会中断异常所在的线程

try:
    try:
        ret = 1/0
    except KeyError as e:  # 未能捕获异常
        print(e)
    finally:
        print('inner finally')
except:
    print('outer catch')
finally:
    print('outer finally')

inner finally
outer catch
outer finally

else子句

没有任何异常发生,则执行else子句

try:
    ret = 1 * 0
except ArithmeticError as e:
    print(e)
else:
    print('ok')
finally:
    print('finally')

总结

try:
  <语句>   # 运行代码
except <异常类>:
  <语句>   # 捕获某种类型异常
except <异常类> as <变量名>:
  <语句>     # 捕获某种类型的异常并获得对象
else:      # 如果没有异常发生执行
  <语句>
finally:     # 退出try总会执行
  <语句>

try工作原理

  • 如果try语句中执行时发生异常,搜索except子句,并执行第一个匹配该异常的except子句。
  • 如果try中语句执行时发生异常,但没有匹配的except子句,则异常会递交给外层try,如果外层也没有处理这个异常,则继续向外传递,如果都不处理,就终止异常所在的线程。
  • 如果在try执行时没有发生异常,将执行else子句中的语句。
  • 无论try中是否发生异常,finally子句最终都会执行。