从零学 Python 系列(七):异常处理与调试,让代码远离 “崩溃”
在前几篇中,我们的程序能处理数据、操作文件了,但有个隐藏的 “雷区”:只要用户输入不符合预期(比如让输入数字却输入了文字),或者文件操作出问题(比如文件被意外删除),程序就会直接报错崩溃。这在实际使用中是不可接受的 —— 一个健壮的程序应该能 “优雅地处理错误”,并给出友好提示。这篇就带你学习 “异常处理” 机制,再掌握几个实用的调试技巧,让你的代码从 “脆弱” 变得 “抗造”。
一、异常:代码运行时的 “意外状况”
先明确一个概念:异常(Exception) 是程序运行时发生的错误,比如语法正确但逻辑出错(比如除以 0、打开不存在的文件)。当异常发生时,Python 会终止程序并显示错误信息(Traceback),这就是我们常说的 “程序崩了”。
1. 常见的异常类型(新手必知)
先认识几种最容易遇到的异常,以后看到错误提示能快速定位问题:
| 异常类型 | 含义 | 例子 |
|---|---|---|
ValueError | 数据类型正确但值不合适 | int("abc")(字符串无法转整数) |
TypeError | 数据类型错误 | "2" + 3(字符串和数字不能相加) |
FileNotFoundError | 找不到文件 | open("不存在的文件.txt") |
ZeroDivisionError | 除以 0 | 10 / 0 |
IndexError | 列表索引越界 | [1,2,3][5](列表只有 3 个元素) |
示例:触发一个异常
# 让用户输入数字,转成整数
num = int(input("请输入一个数字:")) # 如果用户输入"abc",会触发ValueError
print("你输入的数字是:", num)
如果输入 “abc”,程序会报错:
ValueError: invalid literal for int() with base 10: 'abc'
然后直接终止,体验非常差。
二、异常处理:用 try-except 捕获错误
Python 提供了try-except语句来 “捕获” 异常,让程序在出错时不崩溃,而是执行我们指定的处理逻辑(比如提示用户 “输入错误,请重新输入”)。
1. 最基础的 try-except 结构
try:
# 可能发生异常的代码(放在try块里)
代码块
except 异常类型:
# 发生指定异常时,执行这里的代码
处理逻辑
示例:处理 ValueError
try:
num = int(input("请输入一个数字:"))
print("你输入的数字是:", num)
except ValueError:
# 当输入无法转成整数时,执行这里
print("输入错误!请输入有效的数字。")
现在如果输入 “abc”,程序不会崩溃,而是打印 “输入错误!请输入有效的数字。”,更友好了。
2. 捕获多种异常(分情况处理)
如果一段代码可能触发多种异常,可以用多个except块分别处理:
try:
a = int(input("请输入第一个数字:"))
b = int(input("请输入第二个数字:"))
result = a / b
print(f"{a} ÷ {b} = {result}")
except ValueError:
print("输入错误!请输入数字。")
except ZeroDivisionError:
print("错误!除数不能为0。")
测试不同情况:
- 输入 “10” 和 “abc”→ 触发
ValueError→ 提示 “输入错误!” - 输入 “10” 和 “0”→ 触发
ZeroDivisionError→ 提示 “除数不能为 0” - 输入 “10” 和 “2”→ 正常执行→ 输出 “10 ÷ 2 = 5.0”
3. 捕获所有异常(不推荐,但应急可用)
如果不确定会发生什么异常,可以用except Exception捕获所有异常(Exception是所有异常的父类):
try:
# 可能出错的代码
file = open("data.txt", "r")
content = file.read()
file.close()
except Exception as e: # as e 可以获取错误信息
print(f"发生错误:{e}") # 打印错误详情
注意: 这种方式会 “隐藏” 所有错误,包括你没预料到的问题,可能导致调试困难。尽量只捕获你能处理的具体异常,而不是用 “一刀切” 的方式。
4. 可选的 else 和 finally 块
try-except还可以搭配else(无异常时执行)和finally(无论是否有异常都执行):
try:
num = int(input("请输入数字:"))
except ValueError:
print("输入错误")
else:
# 没有异常时,才会执行这里
print(f"你输入的数字是{num},平方是{num**2}")
finally:
# 无论有没有异常,都会执行这里(常用于清理操作,比如关闭文件)
print("--- 操作结束 ---")
运行效果:
- 输入 “5”→ 无异常→ 执行 else 和 finally→ 输出:
你输入的数字是5,平方是25和--- 操作结束 --- - 输入 “abc”→ 有异常→ 执行 except 和 finally→ 输出:
输入错误和--- 操作结束 ---
三、主动抛出异常:用 raise 控制错误
除了处理 “意外发生的异常”,有时我们需要 “主动抛出异常” 来控制程序。比如写一个函数,要求参数必须是正数,如果传入负数,就主动报错。
示例:主动抛出异常
def calculate_square_root(n):
if n < 0:
# 主动抛出ValueError,附带错误信息
raise ValueError("错误:不能计算负数的平方根")
return n **0.5 # 计算平方根
try:
print(calculate_square_root(25)) # 正常:5.0
print(calculate_square_root(-5)) # 触发主动抛出的异常
except ValueError as e:
print(e) # 打印:错误:不能计算负数的平方根
主动抛异常的好处是:在函数内部提前检查错误,让错误信息更明确,方便调用者处理。
四、调试:快速找到代码中的 “bug”
即使学会了异常处理,代码还是可能出各种问题(比如逻辑错误:计算结果不对,但不报错)。这时候就需要 “调试”—— 一步步排查代码,找到错误原因。
1. 最基础的调试方法:print () 打印中间结果
新手最容易上手的方式:在关键步骤用print()输出变量值,看是否符合预期。
示例:调试一个计算平均分的函数
def calculate_average(scores):
print("输入的成绩列表:", scores) # 打印输入数据
total = 0
for score in scores:
total += score
print(f"累加后:{total}(当前分数:{score})") # 打印循环中的中间值
avg = total / len(scores)
print("总分:", total, ",人数:", len(scores), ",平均分:", avg) # 打印计算过程
return avg
# 测试函数
average = calculate_average([85, 92, "78"]) # 这里有个bug:列表里混了字符串
print("最终平均分:", average)
运行后会打印:
输入的成绩列表: [85, 92, '78']
累加后:85(当前分数:85)
累加后:177(当前分数:92)
TypeError: unsupported operand type(s) for +=: 'int' and 'str'
通过打印的中间结果,能快速发现:循环到第三个元素时,score是字符串'78',导致无法累加,从而定位 bug。
2. 更专业的调试工具:PyCharm 断点调试
如果代码很长,print()会显得繁琐。PyCharm 的 “断点调试” 可以让程序 “一步步运行”,随时查看变量值,更高效。
步骤:1.** 打断点 :在代码行的左边点击,出现红色圆点(断点表示程序运行到这里会暂停);2. 启动调试 :右键点击代码文件,选择 “Debug 文件名”;3. 控制运行 :- Step Over(F8):执行当前行,跳到下一行(不进入函数内部);- Step Into(F7):如果当前行是函数调用,进入函数内部;- Resume Program(F9):继续运行到下一个断点;4. 查看变量 **:在底部的 “Debug” 窗口中,能看到所有变量的当前值。
示例:用断点调试平均分函数在for score in scores:这一行打断点,启动调试后,程序会暂停在这里。通过 Step Over 一步步执行,能看到score的值从 85→92→'78',当执行到total += score时,会触发TypeError,此时能清晰看到是'78'导致的错误。
3. 调试常见误区(新手避坑)
-** 不要凭感觉猜 bug :遇到问题先看错误提示(Traceback),定位到报错的行,再检查该行及附近的代码;- 简化问题 :如果代码复杂,先把无关部分删掉,用最小的示例复现问题(比如测试平均分函数时,用[1,2,3]这样的简单数据);- 善用搜索引擎 **:把错误提示(比如TypeError: unsupported operand type(s))复制到百度 / 谷歌,通常能找到解决方案。
五、实战:给学生管理系统添加异常处理
给之前的 “学生信息管理系统” 加上异常处理,解决用户输入错误(比如年龄输入文字、成绩输入负数)的问题。
改进后的核心代码:
def add_student(students):
try:
name = input("请输入姓名:").strip() # 去除首尾空格
if not name: # 检查姓名是否为空
raise ValueError("姓名不能为空!")
age = int(input("请输入年龄:"))
if age < 0 or age > 150: # 检查年龄合理性
raise ValueError("年龄必须在0-150之间!")
score = float(input("请输入成绩:"))
if score < 0 or score > 100: # 检查成绩合理性
raise ValueError("成绩必须在0-100之间!")
students.append({"name": name, "age": age, "score": score})
print(f"已添加学生:{name}")
except ValueError as e:
print(f"添加失败:{e}") # 捕获所有输入相关的错误
except Exception as e:
print(f"发生未知错误:{e}") # 捕获其他意外错误
# 主程序中调用add_student(students)
改进后效果:
- 输入空姓名→ 主动抛异常→ 提示 “姓名不能为空!”
- 年龄输入 “abc”→ 触发
ValueError→ 提示 “添加失败:invalid literal for int () with base 10: 'abc'” - 成绩输入 “150”→ 主动抛异常→ 提示 “成绩必须在 0-100 之间!”
- 所有错误都不会导致程序崩溃,用户可以重新操作。
下一篇预告:面向对象编程入门,代码更 “模块化”
学会了异常处理和调试,你的程序已经足够 “健壮” 了。但如果要开发大型项目(比如一个完整的管理系统),用之前的 “函数 + 数据” 的方式,代码会越来越难维护。下一篇会带你入门 “面向对象编程(OOP)”—— 这是一种更先进的编程思想,通过 “类和对象” 把数据和功能封装在一起,让代码更易扩展、更易复用,是 Python 高级开发的基础。
如果这篇的异常处理技巧帮你解决了代码崩溃的问题,欢迎在评论区分享你的经历:比如你遇到过什么棘手的异常,是怎么解决的,我们一起交流~
更多推荐


所有评论(0)