程序运行过程中,难免会出现语法错误、逻辑错误、外部数据错误等情况。
在 Python3 中,提供了完善的错误与异常处理机制,让程序不会因为意外问题直接崩溃,同时能记录问题的详细日志,方便排查问题。掌握错误和异常机制是编写健壮、稳定程序的必备能力。
错误就是程序不符合语言规则,或运行时出问题,导致无法正常执行。
例如,代码写法不合法(少冒号、缩进错、关键字拼错),Python 解释器看不懂,运行前直接报错,程序根本跑不起来,必须改对程序才能运行。
示例:缺少冒号,直接报错,无法运行
if True
print("hello")运行时抛出如下错误:
File "/home/cloudlab/main.py", line 1
if True
^
SyntaxError: expected ':'异常指代码语法没错,但运行时出现的意外错误,会导致程序中断。比如:
除以 0
列表索引越界
打开不存在的文件
使用没定义的变量
注意,这类错误不是写错语法,而是运行条件出问题,Python 会抛出异常。如果不处理,程序直接崩溃。为了防止程序崩溃,可以使用 try-except 捕获异常,这样程序可以继续运行。
在 Python 中,常见异常如下:
ZeroDivisionError:除零错误
IndexError:列表索引超出范围
KeyError:字典键不存在
FileNotFoundError:文件不存在
TypeError:类型错误
示例:除 0 异常
print(10 / 0)运行时抛出如下错误:
Traceback (most recent call last):
File "/home/cloudlab/main.py", line 1, in <module>
print(10 / 0)
~~~^~~
ZeroDivisionError: division by zero在 Python 中,为了更好地应对程序执行过程中可能出现的异常情况,特地提供了一系列专门的语句来进行异常的捕获与处理。详细内容如下:
try...except 是 Python 中专门用来捕获并处理运行时异常的语句。主要作用如下:
让程序遇到错误不崩溃
对异常进行捕获、记录、处理、恢复
可以提高程序健壮性
语法如下:
try:
# 可能出错的代码
except 异常类型:
# 发生异常时执行的代码示例:处理不能除以 0 的异常
try:
num = 10 / 0 # 会触发异常
except ZeroDivisionError:
print("错误:不能除以 0!")运行结果:
错误:不能除以 0!上面示例捕获了 ZeroDivisionError 异常,如果程序抛出多个异常呢?可以使用多异常捕获。
多异常捕获指的是在编程过程中,针对可能出现的多种不同类型的异常情况进行捕捉处理的机制。当程序执行时,可能会因为各种原因引发不同类型的异常,比如文件读取失败、数据类型转换错误、网络连接中断等等。通过多异常捕获,程序员能够以一种更为灵活且全面的方式来应对这些潜在的问题。
示例:捕获除零错误和索引越界错误,如下:
try:
lst = [1, 2]
print(lst[5]) # IndexError
except ZeroDivisionError:
print("除零错误")
except IndexError:
print("索引越界")运行结果:
索引越界上述示例,通过两个 except 语句分别捕获了 ZeroDivisionError 和 IndexError 错误。那么,如果程序抛出了很多种类的异常,我们则需要编写很多 except 语句来分别应对各种错误,是不是有点麻烦。庆幸的是,你可以一次性捕获所有的异常。就像下面这样:
try:
lst = [1, 2]
print(lst[5])
except:
print("发生未知错误")运行结果:
发生未知错误注意,在编程实践中不被推荐这样做。虽然它看似能全面处理各种可能出现的异常情况,但实际上却存在较大弊端,会隐藏未知错误。当使用捕获所有异常的方式时,程序不管遇到何种异常,都会统一按照既定的处理逻辑执行。
然而,这就导致了在遇到一些未曾预料到的错误时,开发人员无法清晰地了解问题究竟出在哪里。这些未知错误被掩盖,不利于对程序进行调试和优化,可能会在后续引发更为严重的问题,所以在大多数情况下,不建议采用捕获所有异常的方式。
try...except...else 语句是 Python 中用于异常处理的另一种结构。在这种语句结构里,try 代码块和 except 代码块作用不变。特别需要注意的是 else 代码块,它只有在 try 代码块里没有发生任何异常时才会执行。也就是说,一旦 try 代码块顺利执行完毕,没有触发任何异常情况,程序流程便会紧接着进入 else 代码块继续执行其中的代码。
语法如下:
try:
# 可能出错的代码
except 异常:
# 异常处理
else:
# 无异常时执行示例:
try:
result = 10 / 2
except ZeroDivisionError:
print("除零错误")
else:
print("计算成功,结果:", result) # 无异常会执行运行结果:
计算成功,结果: 5.0如果改成 10 / 0,将抛出 ZeroDivisionError 错误:
try:
result = 10 / 0
except ZeroDivisionError:
print("除零错误")
else:
print("计算成功,结果:", result) # 无异常会执行运行结果:
除零错误try...except...finally 是 Python 中功能最完整的异常处理结构。其中 try 与 except 语句的作用保持不变,try 用于包裹可能出现异常的代码,except 用于捕获并处理对应的异常。
而 finally 代码块有着特殊的执行机制,无论程序在运行过程中是否发生异常、出现的异常是否被成功处理,其中的代码都会 100% 被执行。
该结构在实际开发中十分常用,通常用于执行收尾与清理操作,比如关闭打开的文件、断开数据库连接、释放程序占用的系统资源等。
语法如下:
try:
# 可能出错的代码
except:
# 异常处理
finally:
# 必须执行的代码示例:打开 test.txt 文件,使用 read() 读取文件内容,最后在 finally 语句中使用 close() 语句关闭文件句柄,释放资源。
try:
# 打开文件
file = open("test.txt", "r", encoding="utf-8")
# 读取文件内容
content = file.read()
print(content)
except Exception as e:
# 捕获并打印异常信息
print(f"An error occurred: {e}")
finally:
# 关闭文件
file.close()前面介绍了如何使用 try...except 语句捕获和处理异常,除了捕获异常,我们自己还可以手动抛出异常。
在 Python 中,可以通过 raise 语句抛出异常,raise 是 Python 中用于主动抛出异常的关键字,可强制触发内置的异常错误,也可以抛出自定义异常。
抛出异常的核心作用是自定义业务逻辑错误、参数校验、流程中断等场景。
语法如下:
raise 异常类型(提示信息)示例(内置异常):当年龄小于 0 时,使用 raise 语句抛出 ValueError 内置异常
def check_age(age):
if age < 0:
# 主动抛出异常
raise ValueError("年龄不能为负数!")
print("年龄合法")
try:
check_age(-5)
except ValueError as e:
print("捕获异常:", e)运行结果:
捕获异常: 年龄不能为负数!除了抛出内置异常外,raise 还能抛出自定义异常。而自定义异常是继承内置 Exception 类创建的专属异常,专门用于匹配自己的业务规则。
示例(自定义异常):首先定义一个名为 AgeError 的自定义异常,再在业务中通过 raise 语句抛出异常,最后通过 try...except 捕获异常,如下:
# 1. 定义自定义异常(继承 Exception)
class AgeError(Exception):
"""自定义年龄异常:用于年龄不合法的场景"""
pass
# 2. 业务函数 + raise 抛出自定义异常
def check_age(age):
if age < 0 or age > 150:
# 主动抛出自定义异常,附带描述信息
raise AgeError(f"年龄 {age} 不合法,必须在 0~150 之间")
return f"年龄 {age} 合法"
# 3. 调用并捕获异常
try:
check_age(200)
except AgeError as e:
print(f"捕获到自定义异常:{e}")运行结果:
捕获到自定义异常:年龄 200 不合法,必须在 0~150 之间上述示例中,AgeError 异常类没有添加任何逻辑,通过 pass 语句占位。我们可以使用 __init__() 构造方法可以给自定义异常写固定的初始化逻辑,如下:
# 1. 定义自定义异常(继承 Exception)
class AgeError(Exception):
"""自定义年龄异常:用于年龄不合法的场景"""
# 为 message 指定默认信息
def __init__(self, message = "年龄不合法"):
super().__init__(message) # 调用父类构造函数,传递异常信息
# 2. 业务函数 + raise 抛出自定义异常
def check_age(age):
if age < 0 or age > 150:
# 主动抛出自定义异常,附带描述信息
raise AgeError()
return f"年龄 {age} 合法"
# 3. 调用并捕获异常
try:
check_age(200)
except AgeError as e:
print(f"捕获到自定义异常:{e}")运行结果:
捕获到自定义异常:年龄不合法你写代码时,程序一旦出错,Python 不会只说 “错了”,而是会抛出一个有名字、有分类、有含义的错误,这些错误就是内置异常。
比如:
除以 0 → ZeroDivisionError
变量没定义 → NameError
列表下标越界 → IndexError
打开不存在的文件 → FileNotFoundError
上面的这些异常都不是我们自己写的,而是 Python 内置在解释器里的,所以叫内置异常。
在 Python3 中,BaseException 类是所有异常的根类,包含系统异常 + 业务异常。开发者永远不要直接继承 / 捕获它(因为这会拦截 Ctrl+C、系统退出等)。
系统级异常(不建议捕获),如下:
BaseException
├─ SystemExit # 执行 exit()/sys.exit() 触发,程序正常退出
├─ KeyboardInterrupt # Ctrl+C 终止程序触发
├─ GeneratorExit # 生成器关闭时触发
└─ Exception # 业务异常父类下面是 Python 中常用的业务异常类,覆盖了 99% 的业务,了解即可,当程序抛出某个异常,能知道异常代表什么错误就可以了,不需要全部记住:
Exception
├─ StopIteration # 迭代器无更多数据时抛出
├─ StopAsyncIteration # 异步迭代器结束
├─ ArithmeticError # 数学计算错误基类
│ ├─ ZeroDivisionError # 除 0 错误
│ ├─ OverflowError # 数值溢出(Python 极少出现)
│ └─ FloatingPointError # 浮点数计算错误
├─ AssertionError # assert 断言失败
├─ AttributeError # 对象无此属性(如 a.b 不存在)
├─ BufferError # 缓冲区操作失败
├─ EOFError # 读取到文件末尾(未读到数据)
├─ ImportError # 模块导入失败
│ └─ ModuleNotFoundError # 模块找不到(ImportError 子类)
├─ LookupError # 查找/索引错误基类
│ ├─ IndexError # 列表索引越界(如 list[999])
│ └─ KeyError # 字典键不存在
├─ MemoryError # 内存不足
├─ NameError # 使用未定义变量
│ └─ UnboundLocalError # 函数内局部变量未赋值先使用
├─ OSError # 操作系统错误基类
│ ├─ FileNotFoundError # 文件不存在
│ ├─ PermissionError # 权限不足
│ ├─ IsADirectoryError # 操作了目录而非文件
│ ├─ NotADirectoryError # 操作了文件而非目录
│ ├─ FileExistsError # 创建已存在文件
│ └─ TimeoutError # 系统操作超时
├─ ReferenceError # 弱引用对象被回收后访问
├─ RuntimeError # 运行时通用错误(不推荐使用)
│ └─ NotImplementedError # 方法未实现
├─ SyntaxError # 语法错误
│ └─ IndentationError # 缩进错误
├─ SystemError # 解释器内部错误(非致命)
├─ TypeError # 类型错误(如 1 + "str")
├─ ValueError # 值合法但无效(如 int("abc"))
│ └─ UnicodeError # 编码/解码错误
│ ├─ UnicodeEncodeError # 从字节 → 字符串 时出错
│ ├─ UnicodeDecodeError # 从字符串 → 字节 时出错
│ └─ UnicodeTranslateError # 字符串在转换 / 映射过程中出错
└─ Warning # 警告基类(非错误)最后,关于异常的一些核心规则,需要谨记:
自定义异常必须继承 Exception,不要继承 BaseException
捕获顺序,先捕获子类再捕获父类
except Exception 能捕获所有业务错误,但不会捕获 Ctrl+C/ 退出
系统异常(KeyboardInterrupt)不要捕获
程序调试,就是:找出代码哪里错了、为什么错,然后把它修好的整个过程。
在 Windows 中搜索 IDLE,运行 IDLE,如下图:

IDLE 是 Python 自带内置的集成开发与学习环境,无需额外安装,即可开始编写 Python 代码。IDLE 提供简洁易用的界面,对编程新手而言是理想的入门工具。它具备代码编辑、运行和调试等基础功能,整个环境简单易懂,非常适合初学编程的人员使用。
调试步骤如下:
(1)打开 IDLE → 新建文件 → 编写代码,如下图:

代码内容如下(将代码保存到桌面 debug.py 文件):
# 计算两个数的和,并判断结果是否大于10
def debug_demo():
# 1. 定义变量
a = 5
b = 3
# 2. 计算
total = a + b
print("计算结果:", total)
# 3. 条件判断
if total > 10:
print("结果大于10")
else:
print("结果小于等于10")
# 4. 循环演示
for i in range(3):
print("循环次数:", i)
# 调用函数
debug_demo()(2)点击顶部菜单 Debug → Debugger,如下图:

调试窗口如下图:

此时没有任何程序可以调试。
(3)切换到代码编辑器,选择“Run → Run Module”菜单,如下图:

点击“Run Module”后,将自动运行程序且跳转到 Debugger 调试界面,如下图:

此时,可以使用按钮进行控制:
Go:运行到下一个断点
Step:单步执行(一行一行走)
Over:跳过函数
Out:跳出函数
Quit:退出调试
(4)最后,切换到代码编辑器,设置断点开始调试。将光标移动到需要的行,然后右击,打开右键菜单,选择“Set Breakpoint(设置断点)”或者“Clear Breakpoint(清空断点)”,如下图:

我们将在“a = 5”行设置断点,设置后的效果如下图:

此时,跳转到 Debugger 选择“Go”按钮,如下图:

下图演示了调试的步骤:

assert 中文叫断言,是 Python 内置的调试、校验工具。
用来主动检查某个条件必须为真,否则直接抛出 AssertionError 异常,中断程序。该中方式非常适合用来校验参数、检查中间结果、快速定位逻辑错误。
语法如下:
assert 条件, 错误信息示例:使用 assert 来断言除数不能为零,如果除数为 0,则抛出异常
def divide(a, b):
# 断言:b 不能为 0
assert b != 0, "除数不能为 0"
return a / b
divide(10, 0) # 触发 AssertionError运行结果:
Traceback (most recent call last):
File "/home/cloudlab/main.py", line 6, in <module>
divide(10, 0) # 触发 AssertionError
^^^^^^^^^^^^^
File "/home/cloudlab/main.py", line 3, in divide
assert b != 0, "除数不能为 0"
^^^^^^
AssertionError: 除数不能为 0断言专为开发调试设计,正式运行时可关闭断言,适合快速校验参数与程序状态。在 Python 中,关闭断言非常简单,只需要在运行时加一个 -O(大写字母 O)参数即可,如下:
python -O 你的脚本文件名.py更多 Python3 知识,请继续学习后续章节。