Python3 错误和异常详解
在 Python3 中,错误和异常是程序运行中常见的问题。理解它们的区别、掌握处理机制,是编写健壮代码的核心能力。本文将详细解析 Python 的错误类型、异常处理方法及最佳实践。
三、异常处理机制:
4.
5.
四、主动抛出异常:
六、断言:
一、错误与异常的区别
在 Python 中,“错误” 和 “异常” 是两个相关但不同的概念:
-
语法错误(Syntax Error):代码不符合 Python 语法规则,导致解释器无法解析。这是 “错误” 的一种,属于编译阶段的问题,程序根本无法运行。
示例:if a > 5 # 缺少冒号,语法错误 print(a) # 报错:SyntaxError: invalid syntax -
异常(Exception):代码语法正确,但运行时出现的错误。程序可以启动,但执行到特定逻辑时中断。
示例:10 / 0 # 语法正确,但运行时触发“除零异常” # 报错:ZeroDivisionError: division by zero
简言之:语法错误是 “写不对”,异常是 “运行时出问题”。
二、常见的异常类型
Python 内置了多种异常类,用于描述不同场景的运行时错误。以下是最常用的几种:
| 异常类型 | 触发场景 | 示例 |
|---|---|---|
NameError |
使用未定义的变量 | print(undefined_var) |
TypeError |
操作或函数应用于错误类型的对象 | 1 + "2"(int 与 str 无法相加) |
ValueError |
函数参数类型正确但值无效 | int("abc")(字符串无法转为整数) |
ZeroDivisionError |
除法或取模运算中除数为 0 | 5 / 0 |
IndexError |
序列(列表、元组等)索引超出范围 | [1,2,3][5] |
KeyError |
字典中查找不存在的键 | {"name": "Tom"}["age"] |
FileNotFoundError |
打开不存在的文件 | open("nonexistent.txt") |
AttributeError |
访问对象不存在的属性或方法 | "hello".unknown_method() |
AssertionError |
断言语句(assert)的条件为False |
assert 1 == 2 |
三、异常处理机制:try-except 块
当程序可能触发异常时,可通过
try-except块捕获并处理,避免程序直接崩溃。1. 基本语法
try:
# 可能触发异常的代码块
risky_operation()
except 异常类型1:
# 当触发“异常类型1”时执行的处理逻辑
handle_error_type1()
except 异常类型2 as e:
# 捕获“异常类型2”并获取异常信息(e为异常对象)
print(f"发生错误:{e}")
示例:处理除零异常
try:
a = 10 / 0
except ZeroDivisionError as e:
print(f"错误:{e},除数不能为0") # 输出:错误:division by zero,除数不能为0
2. 捕获多个异常
可在一个
try块后接多个except,分别处理不同异常:try:
num = int(input("请输入数字:"))
result = 10 / num
except ValueError:
print("输入错误:请输入有效的整数")
except ZeroDivisionError:
print("计算错误:除数不能为0")
若多个异常的处理逻辑相同,可合并为一行:
try:
# ...
except (ValueError, ZeroDivisionError) as e:
print(f"发生错误:{e}")
3. 捕获所有异常(不推荐)
使用
except Exception可捕获所有非系统退出的异常(不包括KeyboardInterrupt等),但可能掩盖未知错误,谨慎使用:try:
# 风险操作
except Exception as e:
print(f"发生未知错误:{e}")
4. else 子句
else可选,用于定义 “当try块中无异常时执行” 的逻辑:try:
num = int(input("请输入数字:"))
result = 10 / num
except (ValueError, ZeroDivisionError) as e:
print(f"错误:{e}")
else:
# 无异常时执行
print(f"计算结果:{result}")
5. finally 子句
finally可选,无论try块是否发生异常,都会执行(常用于资源清理,如关闭文件、数据库连接):file = None
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
print("文件不存在")
finally:
# 确保文件关闭,即使发生异常
if file:
file.close()
print("文件已关闭")
注:Python 3.3+ 中,可使用with语句更简洁地管理资源(自动释放),替代finally的部分场景:
with open("data.txt", "r") as file: # 退出with块时自动关闭文件 content = file.read()
四、主动抛出异常:raise 语句
除了被动捕获异常,还可通过
raise主动触发指定异常,用于自定义错误检查逻辑。基本语法
raise 异常类型(异常信息)
示例:验证输入合法性
def check_age(age):
if age < 0 or age > 150:
# 主动抛出ValueError
raise ValueError(f"无效年龄:{age},必须在0-150之间")
print(f"年龄有效:{age}")
try:
check_age(200)
except ValueError as e:
print(f"错误:{e}") # 输出:错误:无效年龄:200,必须在0-150之间
五、自定义异常类
当内置异常无法满足需求时,可通过继承
Exception类定义自定义异常(通常继承Exception而非BaseException,避免捕获系统级异常)。定义与使用
# 定义自定义异常(继承Exception)
class ScoreError(Exception):
"""分数必须在0-100之间的异常"""
def __init__(self, score):
self.score = score
super().__init__(f"无效分数:{score},必须在0-100之间")
# 使用自定义异常
def check_score(score):
if not (0 <= score <= 100):
raise ScoreError(score) # 触发自定义异常
print(f"分数有效:{score}")
try:
check_score(150)
except ScoreError as e:
print(f"检查失败:{e}") # 输出:检查失败:无效分数:150,必须在0-100之间
六、断言:assert 语句
assert(断言)用于调试阶段检查条件是否成立,若不成立则触发AssertionError。其语法为:assert 条件, 异常信息 # 条件为False时触发异常
示例:调试时验证变量状态
def calculate_average(numbers):
# 断言:输入列表不能为空(调试用)
assert len(numbers) > 0, "列表不能为空"
return sum(numbers) / len(numbers)
calculate_average([]) # 触发AssertionError: 列表不能为空
注意:断言可通过-O(优化模式)关闭(运行时忽略),因此不能替代异常处理,仅用于调试阶段的内部检查。
七、异常处理的最佳实践
- 精准捕获异常:避免使用
except Exception捕获所有异常,应明确指定需要处理的异常类型,防止掩盖未知错误。 - 提供有用的错误信息:在
except块中输出具体的错误原因(如as e获取的异常信息),便于调试。 - 及时清理资源:通过
finally或with语句确保文件、网络连接等资源在异常发生后正确释放。 - 不滥用异常:异常处理的开销高于普通条件判断,对于可预见的情况(如输入验证),优先使用
if判断而非依赖异常。
反例:# 不推荐:用异常处理替代条件判断 try: num = int(input("请输入数字:")) except: num = 0
正例:
# 推荐:先判断再处理 input_str = input("请输入数字:") num = int(input_str) if input_str.isdigit() else 0 - 自定义异常提高可读性:在复杂业务中,使用自定义异常可使错误类型更清晰,便于调用者处理。
总结
Python 的异常处理机制通过
try-except-else-finally实现了对运行时错误的捕获与处理,结合raise和自定义异常,可构建灵活且健壮的错误处理逻辑。核心原则是:提前预判可能的异常,精准捕获并合理处理,同时避免过度依赖异常替代正常的逻辑判断。掌握这些知识,能显著提升代码的可靠性和可维护性。
浙公网安备 33010602011771号