查看全课大纲(第 34 / 51 节)
- 1.Python 是什么?为什么值得学?
- 2.如何为 Python 编程准备一个“全家桶”环境?
- 3.Python 有哪几种执行方法?
- 4.行与缩进到底有什么用?
- 5.注释有什么用,怎么写?
- 6.变量:如何给数据起个好名字?
- 7.运算符的优先级和常见坑点是什么?
- 8.Python 中,数字和布尔值究竟有哪几种?
- 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
- 10.如何将程序的结果展示给用户或保存下来?
- 11.CSV 文件怎么读怎么写才方便?
- 12.列表是什么,如何创建和访问它?
- 13.列表的修改操作,有哪些方法?
- 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
- 15.判断语句:如何让程序根据条件做不同的事?
- 16.for 循环如何遍历序列中的元素?
- 17.while 循环什么时候才会停?
- 18.while 循环的结束条件,你写对了吗?
- 19.循环中途想提前结束,怎么办?
- 20.`break` 和 `continue` 到底有什么区别?
- 21.列表推导式如何让代码更简洁高效?
- 22.字符串怎么定义、拼接和操作?
- 23.元组:为什么定义单个元素时要加逗号?
- 24.集合(set)怎么用?如何快速去重和做集合运算?
- 25.字典是什么,为什么比列表更方便?
- 26.字典的增删改查与遍历,如何避免 KeyError?
- 27.函数里的变量,为什么有的能用有的不能用?
- 28.在函数内部修改全局变量,为什么有时不生效?
- 29.函数里能修改全局变量吗?返回值怎么用?
- 30.匿名函数:如何用 lambda 快速定义简单函数?
- 31.面向对象编程是什么,为什么需要它?
- 32.类中的方法和属性到底怎么定义和使用?
- 33.如何控制对对象内部属性和方法的访问?
- 34.异常发生时,程序会怎样继续执行?
- 35.如何组织代码:包和模块
- 36.`if __name__ == '__main__'` 到底有什么用?
- 37.如何用 IDE 的 Debug 功能排查程序错误?
- 38.Matplotlib 绘图的第一步该做什么?
- 39.Figure 是什么,如何设置图形尺寸?
- 40.如何自定义 Matplotlib 的坐标轴?
- 41.如何为图表添加图例,让不同曲线一目了然?
- 42.饼图怎么画才能直观展示数据占比?
- 43.如何用 Matplotlib 绘制散点图?
- 44.柱状图怎么画?
- 45.如何使用 Matplotlib 绘制等高线图?
- 46.如何在 Matplotlib 中绘制子图?
- 47.zip 函数如何把多个列表“打包”在一起?
- 48.如何给可迭代对象加上索引?
- 49.random 模块如何生成随机数?
- 50.math 模块怎么用?
- 51.Counter 如何快速统计元素出现次数?
异常发生时,程序会怎样继续执行?
约 19 分钟
小象实战讲义 · Python 编程从入门到实践
在编写程序时,难免会遇到一些意外情况,比如打开一个不存在的文件,或者进行不合法的计算(如除以零)。如果程序直接崩溃,显然不是我们想要的结果。本节学习的异常处理,就是让程序在遇到错误时能够“体面地”应对,而不是直接停止。这是编写健壮程序的关键一环,也是从“基础语法”迈向“实用编程”的重要一步。
💡 核心导读
- 异常是什么:程序执行时因非法操作或主动触发而抛出的错误信号。
- 如何捕获异常:使用
try...except结构,捕获特定或所有类型的异常,并执行相应的处理逻辑。 - 获取异常信息:通过
as关键字将异常对象赋值给变量,可以打印或分析具体的错误信息。 - 确保资源释放:使用
finally关键字,无论是否发生异常,都会执行其中的代码,常用于关闭文件等资源清理工作。 - 异常的传递:函数内抛出的异常会一层层向上传递,直到被某个调用者的
try...except捕获。
异常的产生与抛出
程序运行中,当 Python 解释器遇到无法处理的错误时,就会主动抛出(raise)一个异常。异常一旦抛出,程序会立即中断当前流程,后续代码将不会被执行。
异常的产生主要有两种方式:
- 由解释器自动抛出:例如执行非法操作(除以零、访问未定义的变量、打开不存在的文件等)。
- 由程序主动抛出:使用
raise关键字,可以主动触发一个异常。
# 示例一:捕获由非法操作(除以零)引发的异常
try:
result = 1 / 0 # 这行会引发 ZeroDivisionError
except ZeroDivisionError:
print("捕获到 ZeroDivisionError: 除数不能为零")
# 示例二:捕获由主动 raise 引发的异常
try:
print("Hello One")
raise Exception("这里发生了一个自定义错误") # 主动抛出异常
print("Hello Two") # 这行不会被执行
except Exception as e:
print("捕获到异常: {}".format(e))捕获到 ZeroDivisionError: 除数不能为零
Hello One
捕获到异常: 这里发生了一个自定义错误可以看到,无论是自动还是主动抛出的异常,都会中断 raise 语句之后的代码。Python 内置函数(如 open())在内部遇到错误时,本质上也是通过 raise 将异常抛给我们的。
捕获并处理异常
我们使用 try...except 结构来捕获并处理异常,防止程序直接崩溃。
捕获特定异常,并获取错误信息
将可能出错的代码放在 try: 代码块中。如果其中发生了异常,Python 会尝试匹配 except 后面指定的异常类型;匹配成功,就执行对应的 except 块中的处理代码。使用 except 异常类型 as 变量名,还能把异常对象保存到变量里,打印或记录具体的错误信息。下面用不依赖本地文件的例子演示(比如打开不存在的文件会触发 FileNotFoundError,思路完全相同)。
# 1) 捕获特定异常,并用 as 取出异常对象
try:
number = int("abc") # 触发 ValueError
except ValueError as err:
print("异常类型:", type(err).__name__)
print("异常信息:", err)
# 2) 一个 except 捕获多种异常类型
try:
result = 1 / 0
except (ValueError, ZeroDivisionError) as error:
print("捕获到了异常:", error)
# 3) 用 except Exception 兜底,捕获前面没有处理的其他异常
num = 0 # 模拟用户输入了 0(原程序用 input() 读取)
try:
result = 1 / num
except ValueError:
print("输入的不是数字,这是预期内的错误。")
except Exception as err:
print("发生了未预期的错误: {}".format(err))异常类型: ValueError
异常信息: invalid literal for int() with base 10: 'abc'
捕获到了异常: division by zero
发生了未预期的错误: division by zero通常用 except Exception: 兜底处理前面没有捕获到的“意料之外”的错误。
匹配顺序:Python 会从上到下依次检查 except 块,执行第一个匹配到的异常类型对应的代码块。因此,通常将更具体的异常类型放在前面,将 except Exception: 放在最后。
无论如何都要执行的 finally
finally 块中的代码无论是否发生异常,无论异常是否被捕获,最终都会被执行。这使其成为进行资源清理(如关闭文件、释放网络连接)的理想场所。
import io
resource = None # 预先声明变量
try:
resource = io.StringIO("异常处理演示") # 用一个内存资源代替文件,避免依赖本地文件
print("读取内容:", resource.read())
raise ValueError("读取过程中出现了问题") # 模拟处理过程中抛出的异常
except ValueError as error:
print("捕获到异常: {}".format(error))
finally:
print("执行 finally 块")
if resource: # 检查资源是否被成功创建(不是 None)
resource.close()
print("资源已关闭")读取内容: 异常处理演示
捕获到异常: 读取过程中出现了问题
执行 finally 块
资源已关闭在上面的例子中,try 块里主动抛出了异常,但 finally 依然会执行,从而保证资源一定被清理。
异常在函数调用中的传递
异常具有传递性。当一个函数内部抛出异常且未被捕获时,这个异常会沿着调用链向上“冒泡”,传递给它的调用者。如果调用者也没有捕获,则继续向上,直到被某个 try...except 捕获,或者传递到程序最顶层导致程序崩溃。
def inner_function():
print("------inner_function-1------")
print(undefined_variable) # 这里会抛出 NameError
print("------inner_function-2------") # 这行不会执行
def outer_function():
try:
print("------outer_function-1------")
inner_function() # 调用内部函数
print("------outer_function-2------") # 因为内部函数抛出异常,这行不会执行
except Exception as error:
print("在 outer_function 中捕获到了异常: {}".format(error))
print("------outer_function-3------") # 异常被捕获后,程序继续执行
outer_function()------outer_function-1------
------inner_function-1------
在 outer_function 中捕获到了异常: name 'undefined_variable' is not defined
------outer_function-3------这个特性使得我们可以在合适的层级(比如主逻辑或调用者)集中处理错误,而不必在每个可能出错的低级函数中都写 try...except。
📝 动手练一练
- 编写一个安全的除法计算器:要求用户输入两个数字,计算它们的商。使用异常处理来应对用户输入非数字、除数为零等情况,并给出友好的提示信息。
👉 点击查看参考答案
思路:使用 try...except 包裹核心计算逻辑。ValueError 捕获输入非数字的异常,ZeroDivisionError 捕获除数为零的异常。
# 注意:这里使用固定值演示。实际运行时可将 fixed_a, fixed_b 替换为 input()
fixed_a = "10" # 模拟用户输入,实际应为 input("请输入被除数: ")
fixed_b = "0" # 模拟用户输入,实际应为 input("请输入除数: ")
try:
a = float(fixed_a)
b = float(fixed_b)
result = a / b
print("{} / {} = {}".format(a, b, result))
except ValueError:
print("错误:请输入有效的数字。")
except ZeroDivisionError:
print("错误:除数不能为零。")错误:除数不能为零。常见错误
| 错误写法 | 现象或报错 | 原因 | 正确写法 |
|---|---|---|---|
except: (后面无异常类型) | 虽然能捕获几乎所有运行时异常,但会隐藏真正的问题,不利于排查。 | 过于宽泛,掩盖了真正的问题。 | 明确指定要捕获的异常类型,或用 except Exception: 作为兜底。 |
except (ValueError, TypeError)print("出错") | SyntaxError: expected ':' | except 子句末尾缺少冒号 :。 | except (ValueError, TypeError):print("出错") |
将 except Exception: 放在最前面 | 后面的 except ValueError: 等永远不会被执行。 | Exception 匹配所有异常,第一个匹配到的 except 块会执行。 | 将具体的异常类型(如 ValueError)放在前面,except Exception: 放在最后。 |
f = open('file.txt')# ... 操作文件# 忘记 f.close() | 程序运行时可能无报错,但会持续占用系统文件描述符,造成资源泄漏。 | 未在 finally 中确保文件关闭,造成资源泄漏。 | 使用 try...finally 确保文件关闭。 |
小结
- 异常是程序运行时发生的错误信号,会中断正常执行流程。
- 使用
try:包裹可能出错的代码,用except 异常类型:来捕获并处理特定异常。 - 通过
except ... as err可以获取异常对象,查看具体错误信息。 - 一个
try可对应多个except,按顺序匹配。except Exception:可捕获所有未被前面处理的其他异常。 finally:块中的代码无论是否发生异常都会执行,常用于资源清理。- 函数内未被捕获的异常会向上一层层的调用者传递,直到被捕获或程序崩溃。
📋 行动清单
- 将本节所有示例代码手动输入并运行一遍,观察异常被捕获前后的程序输出差异。
- 修改“捕获多种异常”的示例,尝试触发
NameError,观察输出变化。 - 编写一个小程序,用
try...except...finally演示:无论try里是否出错,finally中的清理动作都会执行。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载第4章代码包:异常(含1 个 Notebook)ZIP · 1KB