从零开始学Python(八):异常处理

从零开始学Python(八):异常处理

本系列教程将带你从零开始学习Python编程,无需任何编程基础。

什么是异常?

异常(Exception)是程序运行过程中发生的错误。如果不处理异常,程序会崩溃并显示错误信息。常见的异常包括除以零、访问不存在的列表索引、访问不存在的字典键、类型错误、文件不存在等。

基本异常处理

使用 try-except 语句捕获和处理异常。基本语法是将可能产生异常的代码放在try块中,异常发生时执行的代码放在except块中。可以捕获特定类型的异常,比如ZeroDivisionError、ValueError等,也可以捕获所有异常。

捕获多个异常时,应该按照从具体到一般的顺序排列。捕获所有异常虽然有时有用,但不推荐,因为可能隐藏重要的错误信息。

else 和 finally

else块在try块没有发生异常时执行,常用于正常情况下的处理逻辑。finally块无论是否发生异常都会执行,常用于清理资源,比如关闭文件、释放锁等。

完整的异常处理结构包括try、except、else、finally四个块,其中else和finally是可选的。

获取异常信息

可以捕获并显示异常的详细信息。使用as关键字将异常对象赋值给变量,可以访问异常的类型和信息。type(e).__name__获取异常类型,e获取异常描述信息。

实战示例:输入验证器

让我们创建一个实用的输入验证程序,验证用户输入的数据是否符合要求,并提供友好的错误提示。

创建文件 input_validator.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# 输入验证器

def get_valid_name(prompt):
"""获取有效的姓名"""
while True:
try:
name = input(prompt).strip()

if not name:
raise ValueError("姓名不能为空")

if len(name) < 2:
raise ValueError("姓名至少需要2个字符")

if not name.replace(" ", "").isalpha():
raise ValueError("姓名只能包含字母和空格")

return name

except ValueError as e:
print(f"❌ 输入无效:{e}")
print("请重新输入\n")

def get_valid_age(prompt):
"""获取有效的年龄"""
while True:
try:
age_str = input(prompt).strip()

if not age_str:
raise ValueError("年龄不能为空")

age = int(age_str)

if age < 0:
raise ValueError("年龄不能为负数")

if age > 150:
raise ValueError("年龄不能超过150")

return age

except ValueError as e:
if "invalid literal" in str(e):
print("❌ 输入无效:请输入有效的数字")
else:
print(f"❌ 输入无效:{e}")
print("请重新输入\n")

def get_valid_email(prompt):
"""获取有效的邮箱"""
while True:
try:
email = input(prompt).strip()

if not email:
raise ValueError("邮箱不能为空")

if "@" not in email or "." not in email:
raise ValueError("邮箱格式不正确(必须包含 @ 和 .)")

if email.count("@") != 1:
raise ValueError("邮箱只能包含一个 @")

return email

except ValueError as e:
print(f"❌ 输入无效:{e}")
print("请重新输入\n")

def get_valid_score(prompt, subject):
"""获取有效的分数"""
while True:
try:
score_str = input(prompt).strip()

if not score_str:
raise ValueError(f"{subject}分数不能为空")

score = float(score_str)

if score < 0:
raise ValueError(f"{subject}分数不能为负数")

if score > 100:
raise ValueError(f"{subject}分数不能超过100")

return score

except ValueError as e:
if "invalid literal" in str(e):
print("❌ 输入无效:请输入有效的数字")
else:
print(f"❌ 输入无效:{e}")
print("请重新输入\n")

def calculate_grade(scores):
"""计算成绩等级"""
avg = sum(scores.values()) / len(scores)
if avg >= 90:
return "优秀", avg
elif avg >= 80:
return "良好", avg
elif avg >= 60:
return "及格", avg
else:
return "不及格", avg

def main():
"""主程序"""
print("=" * 50)
print(" 学生信息录入系统")
print("=" * 50)
print()

# 获取学生信息
name = get_valid_name("请输入学生姓名:")
age = get_valid_age("请输入学生年龄:")
email = get_valid_email("请输入学生邮箱:")

print("\n" + "-" * 50)
print("请输入各科成绩(0-100):")
print("-" * 50)

# 获取各科成绩
scores = {}
scores["语文"] = get_valid_score("语文成绩:", "语文")
scores["数学"] = get_valid_score("数学成绩:", "数学")
scores["英语"] = get_valid_score("英语成绩:", "英语")

# 计算等级
grade, average = calculate_grade(scores)

# 显示结果
print("\n" + "=" * 50)
print(" 学生信息录入成功!")
print("=" * 50)
print(f"\n姓名:{name}")
print(f"年龄:{age}岁")
print(f"邮箱:{email}")
print("-" * 50)
print("各科成绩:")
for subject, score in scores.items():
print(f" {subject}{score}分")
print("-" * 50)
print(f"平均分:{average:.2f}分")
print(f"等级:{grade}")
print("=" * 50)

if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n程序已被用户中断")
except Exception as e:
print(f"\n程序发生错误:{e}")
finally:
print("\n感谢使用!")

这个程序使用异常处理验证用户输入,每个输入函数都有自己的验证逻辑。使用raise ValueError主动抛出异常,使用try-except捕获并处理输入错误,提供清晰的错误提示引导用户正确输入,主程序使用try-except-finally处理整体异常。

主动抛出异常

使用 raise 语句主动抛出异常。这在验证输入参数时很有用,如果参数不符合要求,可以抛出异常提醒调用者。

自定义异常

可以创建自己的异常类,继承Exception类。自定义异常可以提供更具体的错误信息,使代码更清晰。

异常处理最佳实践

应该捕获具体的异常类型,而不是所有异常。提供有用的错误信息,帮助用户理解问题和解决方法。适当使用finally块确保资源被释放。不要使用异常进行常规流程控制,应该使用条件判断等正常方式。


系列导航