学习目标

了解异常的概念
掌握捕获指定异常、自定义异常
掌握断言及上下文管理
熟悉使用IDLE、pdb调试代码

异常与错误

程序运行时常常会引发错误,引发错误的原因有很多,例如除零、下标越界、文件不存在、网络异常、类型错误、名字错误、字典键错误、磁盘空间不足,等等。
如果这些错误得不到正确的处理将会导致程序终止运行,而合理地使用异常处理可以使得程序更加健壮,具有更强的容错性,不会因为用户不小心的错误输入或其他运行时原因而造成程序终止。
也可以使用异常处理结构为用户提供更加友好的提示。
程序出现异常或错误之后是否能够调试程序并快速定位和解决问题也是程序员综合水平和能力的重要体现方式之一。
Python至少有两类不同的错误:

  • 语法错误(Syntax Errors)
  • 异常(Exceptions)

语法错误也叫解析错误,是我们最不愿意发生的错误。

  1. '''
  2. 例子中的语法错误就是在print前少加了冒号
  3. '''
  4. while True print('Hello World')
  5. '''
  6. SyntaxError: invalid syntax
  7. '''

程序执行过程中,Python解释器会检测你的程序是否存在语法错误,如果程序出错Python解释器会指出出错的一行,并且在最先找到的错误的文字进行标记颜色或小箭头。
一个语句或者一个表达式即使编译时是没有语法错误的,但是也有可能在执行时出现问题,这种问题也叫异常(非致命性)。
异常通常都是在程序中进行处理的。
异常的分类:

  • ZeroDivisionError、NameError、TypeError等这类异常称为标准异常。
  • 还有一类异常是用户自定义的。

异常是一个事件,该事件会在程序执行过程中发生,影响程序的正常执行。一般情况下,在Python无法正常处理程序时就会抛出一个异常。
异常是Python的对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
在Python中,所有的异常类都是Exception的子类,且都在exceptions模块中定义。Python自动将所有异常名称放在内建命名空间中,所以程序不必导入exceptions模块即可使用异常。

Python常见异常类型表

异常名称 描述
Exception 常规错误的基类
BaseException 所有异常的基类
ZeroDivisionError 除(或取模)零(所有数据类型)
NameError 未声明/初始化对象(没有属性)
SyntaxError 语法错误
IndexError 序列中没有此索引(index)
KeyError 映射中没有这个键
FileNotFoundError 文件未找到
AttributeError 对象没有这个属性
ValueError 传入无效参数

异常处理

Python解释器检测到程序出现的错误,触发异常,需要编写特定的代码去进行异常处理,用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关)。如果捕捉成功则进入另外一个处理分支,执行为其定制逻辑,使程序不会崩溃,这就是异常处理。

异常处理的关键字

关键字 说明
try / except 捕获异常并处理
pass 忽略异常
as 定义异常实例(except MyError as e)
else 如果try中的语句没有引发异常,则执行else中的语句
finally 无论是否出现异常,都执行的代码
raise 抛出/引发异常

捕获指定异常

Python的异常捕获常用try … except结构,把可能发生错误的语句放在try模块里,用except来处理异常,每一个try都必须至少对应一个except。

  1. try:
  2. <可能引发异常的代码块>
  3. # as 别名可省略
  4. except<异常名>[as 别名]:
  5. <出现异常时执行的语句>
  1. '''
  2. try子句打印一个不存在的字符串的索引值,except试图捕获这个异常。
  3. '''
  4. s = 'Hello Python!'
  5. try:
  6. print(s[100])
  7. except IndexError:
  8. print('IndexError...')
  9. print('Continue')
  10. '''
  11. IndexError...
  12. Continue
  13. '''

如果没有对异常进行任何预防,那么在程序执行的过程中发生无索引异常,就会中断程序,并在终端输出异常信息。
反之,程序执行时进入try语句块,发生IndexError异常,会寻找后面时候有except语句。找到except语句后,except将捕获这个异常,处理完毕后,程序继续往下执行。这种情况下,不会中断程序。

捕获多个异常

捕获多个异常有三种方式:

  1. 一个except同时处理多个异常类型,不区分优先级

    1. try:
    2. <可能引发异常的代码块>
    3. except(<异常类型1>, <异常类型2>, ...):
    4. <异常处理代码块>
  2. 区分异常类型的优先级

    1. try:
    2. <可能引发异常的代码块>
    3. except <异常类型1>:
    4. <异常处理代码块>
    5. except <异常类型2>:
    6. <异常处理代码块>

    异常处理结构的规则是:

  • 执行try下的语句,如果引发异常,则执行过程中会跳到第一个except语句;
  • 如果第一个except中定义的异常与引发的异常匹配,则执行该except中的语句;
  • 如果引发的异常不匹配第一个except,则会搜索第二个except,允许编写的except数量没有限制;
  • 如果所有的except都不匹配,则异常会传递到下一个调用本代码的最高层try代码中。 ```python try: num1 = input(‘请输入第1个数:’) num2 = input(‘请输入第2个数:’) print(int(num1) / int(num2)) except ZeroDivisionError: print(‘第2个数不能为0’) except ValueError: print(‘只能输入数字’)

‘’’ 请输入第1个数:a 请输入第2个数:1 只能输入数字 ‘’’

  1. 3. 捕获所有类型的异常
  2. ```python
  3. try:
  4. <可能引发异常的代码块>
  5. except:
  6. <异常处理代码块>

未捕获到异常

带else子句的异常处理结构也是一种特殊形式的选择结构。
如果try中的代码抛出了异常,并且被某个except捕获,则执行相应的异常处理代码,这种情况下不会执行else中的代码;如果try中的代码没有抛出任何异常,则执行else子句中的代码块。
如果使用else子句,那么必须放在所有的except子句之后,这个else子句将在try子句没有发生任何异常的时候执行。

  1. try:
  2. <可能引发异常的代码块>
  3. except<异常类型1>:
  4. <异常处理代码块>
  5. ...
  6. else:
  7. # try语句中没有异常则执行此段代码
  8. <代码块>
  1. s = '5'
  2. try:
  3. int(s)
  4. except Exception as e:
  5. print(e)
  6. else:
  7. print('No Exception')
  8. '''
  9. No Exception
  10. '''

try … except … finally语句无论是否发生异常,都将会执行最后的finally子句中的语句块,通常是进行清理工作。

  1. try:
  2. ......
  3. finally:
  4. # 无论如何都会执行
  5. ......
  1. s = 'Python'
  2. try:
  3. int(s)
  4. except Exception as e:
  5. print('数据异常')
  6. else:
  7. print('try内代码块没有异常则执行')
  8. finally:
  9. print('无论异常与否,都会执行该finally语句')
  10. '''
  11. 数据异常
  12. 无论异常与否,都会执行该finall语句
  13. '''

自定义异常

在引发异常时,应该选择合适的异常类,从而可以明确地描述该异常情况。

主动抛出异常

在程序设计中可以根据需要自行设置异常触发条件,并使用raise语句定义和抛出异常。
raise Exception(args)
参数说明:

  • 语句中Exception是异常的类型(例如ValueError)
  • args是可选的异常参数值,如果不提供,异常的参数是“None” ```python import traceback def fun(leavel): if leavel < 1:
    1. raise Exception('参数错误')
    try: fun(0) except Exception as e: traceback.print_exc() print(e) else: print(‘没有错误’)

‘’’ 参数错误

Traceback (most recent call last): File “C:\Users\ADMINI~1.NZ3\AppData\Local\Temp/ipykernel_9948/1992523584.py”, line 6, in fun(0) File “C:\Users\ADMINI~1.NZ3\AppData\Local\Temp/ipykernel_9948/1992523584.py”, line 4, in fun raise Exception(‘参数错误’) Exception: 参数错误 ‘’’

  1. <a name="yp83r"></a>
  2. # 断言与上下文管理
  3. 断言与上下文管理是两种特殊形式的异常处理方式。<br />它们在形式上比异常处理结构更加简洁,同时可以满足一般的异常处理或条件确认,并且可以与标准的异常处理结构联合使用。
  4. <a name="QqoKZ"></a>
  5. ## 断言
  6. 断言在程序设计中用于表达某个对象的某种判断条件,如果不成立则报错。<br />`assert condition, data`<br />等价于:<br />`if not expression: raise AssertionError`<br /> 说明:
  7. - 当判断条件表达式condition为真时,什么都不做。
  8. - data通常是一个字符串,当表达式的结果为False时,作为异常类型的描述信息使用。
  9. - 如果表达式为假,则抛出异常。
  10. - assert语句一般用于开发程序时对特定必须满足的条件进行验证,仅当__debug__为True时有效。
  11. ```python
  12. a = int(input('请输入a:'))
  13. b = int(input('请输入b:'))
  14. try:
  15. assert a == b, 'a must be equal to b'
  16. except AssertionError as reason:
  17. print('a must be equal to b')
  18. '''
  19. 请输入a:1
  20. 请输入b:2
  21. a must be equal to b
  22. '''

上下文管理

使用上下文管理语句with可以自动管理资源,在代码块执行完毕后自动还原进入该代码块之前的现场或上下文。

  1. with context_expr [as var]:
  2. with语句块
  1. with open('myfile.txt') as f:
  2. for line in f:
  3. print(line, end = '')

程序调试

  • 使用IDLE调试代码
  • 使用pdb调试代码

    使用IDLE调试代码

    Python标准开发环境IDLE可以直接进行代码调试。
    IDLE可以支持在命令行直接进行调试,也可以在程序编辑状态进行断点设置和调试。
    进入调试状态以后,程序会处于暂停状态,直到按下下列五个按钮:

  • Go:程序正常执行至终止,或到达一个“断点”。

  • Step:执行下一行代码,如果下一行代码是一个函数调用,调试器会进入函数内部继续操作。
  • Over:执行下一行代码,如果下一行代码是函数调用,将越过该函数内部的代码。
  • Out:从当前的函数调用中跳出来。
  • Quit:用于停止调试,不必继续执行剩下的程序,就点击Quit按钮。

    使用pdb调试代码

    pdb是Python自带的交互式代码调试模块,代码文件为pdb.py,需要导入后才能使用其中的功能。
命令 说明
h (help)帮助
w (where)打印当前执行堆栈
d (down)执行跳转到在当前堆栈的深一层
u (up)执行跳转到当前堆栈的上一层
b (break)添加断点
b line_no:当前脚本的line_no行添加断点
b filename:line_no:脚本filename的line_no行添加断点
b function:在函数function的第一条可执行语句处添加断点
cl (clear)清除断点
cl清除所有断点
cl bpnumber1 bpnumber2 … 清除断点好为bpnumber1, bpnumber2 … 的断点
cl lineno 清除当前脚本lineno行的断点
cl filename:line_no 清除脚本filename的line_no行的断点
disable 停用断点,参数为bpnumber,和cl的区别是,断点依然存在,只是不启用
enable 激活断点,参数为bpnumber
s (step)执行下一条命令
n (next)执行下一条语句
如果本句是函数调用,则执行函数,接着执行当前语句的下一句
r (return)执行当前运行函数到结束
c (continue)继续执行,直到遇到下一条断点
l (list)列出源码l列出当前执行语句周围11条代码
l first 列出first行周围11条代码
l first second 列出first—second范围的代码,如second < first,second将被解析为行数
a (args)列出当前执行函数的函数
p expression (print)输出expression的值
run 重新启动debug,相当于restart
q (quit)退出debug

在Windows系统下打开Dos命令窗口,或者在运行窗口中输入cmd命令,点击确定打开Dos命令窗口。
进入C:\Temp目录,并输入一下命令:
python -m pdb test.py
即可进入pdb的交互式模式。

总结

异常可以增强系统的容错性
具体掌握try … except,try … except …else,try … except … finally的用法
表达异常的方法有多分支结构、以元组的形式表达
自定义异常处理时对标准异常的有力补充
raise方法是一种常见的异常处理方式
断言适合于各类程序设计中使用