在Python中,异常处理是应对程序运行时错误的核心机制,断言是调试阶段的条件检查工具,引发异常则允许我们主动抛出自定义或内置异常。本文将从基础到进阶,结合实例详细讲解这三部分内容。
一、Python异常处理
异常是程序运行时发生的非预期错误(如除零、文件不存在、类型错误等),若不处理会导致程序崩溃。Python通过try-except-finally-else语句实现异常捕获和处理。
1. 异常的基本概念
Python内置了大量异常类,常见的有:
- ZeroDivisionError:除零错误
- TypeError:类型不匹配
- ValueError:值无效
- FileNotFoundError:文件未找到
- IndexError:索引越界
- KeyError:字典键不存在
所有内置异常均继承自BaseException,核心业务异常通常继承自Exception(开发中主要处理此类)。
2. 异常处理的核心语法
Python提供了4个关键字实现异常处理,组合使用可覆盖绝大多数场景。
(1)基础:try-except
捕获指定异常并处理,语法:
try:
# 可能引发异常的代码块
risky_code
except 异常类型1 [as 变量名]:
# 处理异常类型1的逻辑
except 异常类型2 [as 变量名]:
# 处理异常类型2的逻辑
实例1:捕获单一异常
try:
result = 10 / 0 # 除零操作,触发ZeroDivisionError
except ZeroDivisionError as e:
print(f"异常发生:{e}") # 输出:异常发生:division by zero
实例2:捕获多个异常
try:
num = int("abc") # 类型转换失败,触发ValueError
# result = 10 / 0 # 若打开此行,触发ZeroDivisionError
except ZeroDivisionError as e:
print(f"除零错误:{e}")
except ValueError as e:
print(f"值错误:{e}") # 输出:值错误:invalid literal for int() with base 10: 'abc'
(2)捕获所有异常
若不确定可能的异常类型,可使用except Exception as e捕获所有业务异常(不建议用裸except:,会捕获KeyboardInterrupt等系统异常,导致程序无法正常退出)。
try:
lst = [1, 2, 3]
print(lst[5]) # 索引越界,触发IndexError
except Exception as e:
print(f"捕获到异常:{type(e).__name__},信息:{e}") # 输出:捕获到异常:IndexError,信息:list index out of range
(3)else子句:无异常时执行
若try块未触发任何异常,会执行else子句的代码(可选)。
try:
result = 10 / 2
except ZeroDivisionError as e:
print(f"异常:{e}")
else:
print(f"计算成功,结果:{result}") # 输出:计算成功,结果:5.0
(4)finally子句:始终执行
无论try块是否触发异常,finally子句的代码都会执行,常用于资源清理(如关闭文件、数据库连接)。
try:
f = open("test.txt", "r") # 尝试打开不存在的文件
content = f.read()
except FileNotFoundError as e:
print(f"异常:{e}")
finally:
# 无论是否异常,都关闭文件(若文件对象存在)
if 'f' in locals():
f.close()
print("文件已关闭")
print("程序继续执行")
3. 异常处理的嵌套
可在except或finally块中嵌套try-except,处理嵌套代码的异常。
try:
result = 10 / 2
try:
lst = [1, 2, 3]
print(lst[5])
except IndexError as e:
print(f"内层异常:{e}")
except ZeroDivisionError as e:
print(f"外层异常:{e}")
finally:
print("嵌套异常处理完成")
二、断言(Assert)
断言是Python的调试工具,通过assert语句检查某个条件是否为True,若为False则抛出AssertionError异常。仅用于调试阶段,不建议用于生产环境的业务逻辑验证。
1. 断言的语法
assert 条件表达式 [, 异常提示信息]
- 若
条件表达式为True,程序继续执行; - 若为
False,触发AssertionError,并输出可选的提示信息。
2. 断言的使用实例
实例1:检查输入值的范围
age = -5
# 断言年龄必须大于0,否则抛出异常
assert age > 0, "年龄必须是正整数"
运行结果:
AssertionError: 年龄必须是正整数
实例2:调试时检查函数参数
def calculate_average(scores):
# 断言分数列表非空
assert len(scores) > 0, "分数列表不能为空"
return sum(scores) / len(scores)
# 测试
print(calculate_average([])) # 触发AssertionError
3. 断言的注意事项
- 断言可被关闭:运行Python程序时加
-O参数(优化模式),会忽略所有assert语句,因此不要用断言验证用户输入; - 仅用于调试:断言的作用是“确认程序的内部状态符合预期”,而非处理业务异常(如参数校验应使用
if+raise)。
三、引发异常(Raise)
除了Python自动抛出的异常,我们还可以通过raise语句主动引发异常,用于自定义错误场景(如参数不符合要求、业务规则被违反等)。
1. 引发内置异常
直接通过raise抛出Python内置异常,可指定异常提示信息。
语法1:仅抛出异常类
raise 异常类型
语法2:抛出异常实例(带提示信息)
raise 异常类型("自定义提示信息")
实例1:主动抛出值错误
num = int(input("请输入1-10的数字:"))
if num < 1 or num > 10:
# 主动抛出ValueError
raise ValueError(f"输入的数字{num}超出1-10的范围")
print(f"你输入的数字是:{num}")
实例2:重新引发异常
在except块中,可通过raise重新抛出捕获的异常(用于异常透传或补充信息)。
try:
result = 10 / 0
except ZeroDivisionError as e:
print("记录异常日志:除零错误")
raise # 重新抛出原异常,不修改异常信息
2. 自定义异常类
当Python内置异常无法满足业务需求时,可自定义异常类,必须继承自Exception(而非BaseException)。
自定义异常的语法
class 自定义异常类名(Exception):
"""异常说明文档"""
# 可自定义初始化方法或其他方法
def __init__(self, message):
self.message = message
super().__init__(self.message)
实例:自定义业务异常
# 定义自定义异常:分数超出范围异常
class ScoreOutOfRangeError(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 ScoreOutOfRangeError(score)
print(f"分数{score}验证通过")
# 测试
try:
check_score(105)
except ScoreOutOfRangeError as e:
print(f"捕获到自定义异常:{e}") # 输出:捕获到自定义异常:分数105超出0-100的范围
四、综合实战案例
结合异常处理、断言、引发异常实现一个成绩管理程序,功能包括:输入学生成绩、验证成绩范围、计算平均分。
# 自定义异常:空列表异常
class EmptyScoreListError(Exception):
"""当分数列表为空时触发的异常"""
pass
def input_scores():
"""输入学生成绩,返回成绩列表"""
scores = []
while True:
s = input("请输入学生成绩(输入q结束):")
if s.lower() == 'q':
break
try:
score = float(s)
# 主动抛出异常:成绩超出范围
if not (0 <= score <= 100):
raise ValueError(f"成绩{score}无效,必须在0-100之间")
scores.append(score)
except ValueError as e:
print(f"输入错误:{e}")
continue
return scores
def calculate_average(scores):
"""计算平均分,使用断言和自定义异常"""
# 断言:分数为数字类型(调试用)
for score in scores:
assert isinstance(score, (int, float)), "分数必须是数字"
# 主动抛出自定义异常:空列表
if len(scores) == 0:
raise EmptyScoreListError("分数列表为空,无法计算平均分")
return sum(scores) / len(scores)
# 主程序
if __name__ == "__main__":
try:
scores = input_scores()
avg = calculate_average(scores)
except EmptyScoreListError as e:
print(f"程序异常:{e}")
else:
print(f"学生成绩平均分:{avg:.2f}")
finally:
print("成绩计算程序执行完毕")
五、注意事项
- 避免裸
except:不要使用except:捕获所有异常,会导致无法中断程序(如Ctrl+C的KeyboardInterrupt),应使用except Exception as e; - 断言的局限性:断言仅用于调试,生产环境需用
if+raise验证业务逻辑; - 自定义异常规范:自定义异常类名以
Error结尾,继承自Exception,并添加清晰的提示信息; finally的使用:finally中避免返回值,否则会覆盖try/except中的返回值;- 异常的粒度:捕获异常时应尽量指定具体的异常类型,而非笼统的
Exception。