Python: 异常处理
程序运行中总会出现意外情况——文件不存在、网络断开、用户输入了非法数据……如果不处理这些"异常",程序就会崩溃退出。异常处理让你的程序能从错误中恢复,给用户友好的提示,而不是留下一堆看不懂的错误信息。
1. 什么是异常
异常(Exception)就是程序运行时发生的错误。遇到异常而没处理,程序就会终止:
PYTHON
# 这段代码会崩溃
# print(10 / 0) # ZeroDivisionError: division by zero
# int("abc") # ValueError: invalid literal for int()
# open("不存在的文件.txt") # FileNotFoundError
用 try-except 可以优雅地处理这些错误:
▶ 示例:try-except 处理除零和输入错误
PYTHON
try:
number = int(input("请输入一个数字:"))
result = 10 / number
print(f"10 ÷ {number} = {result}")
except ValueError:
print("输入的不是有效数字!")
except ZeroDivisionError:
print("不能除以零!")
运行示例:
TEXT
📖 仅展示
请输入一个数字:abc
输入的不是有效数字!
💡 异常处理的哲学: 不是用
try 去"预测所有可能的错误",而是"先把事情做了,遇到问题再处理"。Python 社区的说:"It's easier to ask for forgiveness than permission"(EAFP——请求原谅比请求许可更容易)。
2. try-except 的完整结构
▶ 示例:try-except-else-finally 完整结构
PYTHON
try:
# 可能出错的代码
file = open("data.txt", "r", encoding="utf-8")
content = file.read()
number = int(content)
print(f"数字是:{number}")
except FileNotFoundError:
print("文件不存在!")
except ValueError:
print("文件内容不是有效的数字!")
else:
# 没有异常时执行
print("读取并转换成功!")
finally:
# 不管有没有异常都执行
print("清理工作:关闭文件")
try:
file.close()
except NameError:
pass
输出:
TEXT
📖 仅展示
文件不存在!
文件内容不是有效的数字!
读取并转换成功!
清理工作:关闭文件
| 部分 | 什么时候执行 | 用途 |
|---|---|---|
try |
总是 | 放可能出错的代码 |
except |
发生指定异常时 | 处理错误 |
else |
没有异常时 | 成功后的逻辑 |
finally |
总是(不管有没有异常) | 清理资源(关闭文件/释放连接) |
💡 实际开发中,
else 用得比较少,finally 用得很多。 with 语句(上下文管理器)能自动处理资源释放,很多时候可以替代 finally。
3. 常见异常类型
PYTHON
# ZeroDivisionError —— 除零
# int("abc") # ValueError —— 值错误
# [1, 2][5] # IndexError —— 索引越界
# {"a": 1}["b"] # KeyError —— 键不存在
# open("x.txt") # FileNotFoundError —— 文件不存在
# 10 + "hello" # TypeError —— 类型错误
# import nonexistent # ModuleNotFoundError —— 模块不存在
(1) 捕获所有异常(但不推荐)
▶ 示例:捕获异常的正确与错误方式
PYTHON
try:
# 一些代码
pass
except: # ❌ 捕获所有异常
print("出错了")
try:
pass
except Exception as e: # ✅ 捕获已知的所有异常
print(f"出错了:{e}")
输出:
TEXT
📖 仅展示
出错了
⚠️ 裸
except: 不推荐—— 它会捕获 KeyboardInterrupt(Ctrl+C)和 SystemExit 等系统异常,导致程序无法正常终止。用 except Exception as e 更合适。最好的做法是只捕获你预期会发生的具体异常类型。
4. raise:主动抛出异常
有时候你需要主动告诉调用方"出错了",用 raise:
▶ 示例:raise 抛出异常
PYTHON
def divide(a, b):
if b == 0:
raise ValueError("除数不能为0!")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print(f"错误:{e}") # 错误:除数不能为0!
输出:
TEXT
📖 仅展示
# 函数定义成功
(1) 密码强度检查示例
▶ 示例:raise 检查密码强度
PYTHON
def validate_password(password):
"""验证密码强度,不符合条件则抛出异常"""
if len(password) < 6:
raise ValueError("密码长度至少6位")
if not any(c.isdigit() for c in password):
raise ValueError("密码必须包含数字")
if not any(c.isalpha() for c in password):
raise ValueError("密码必须包含字母")
return True
try:
validate_password("123")
except ValueError as e:
print(f"密码不合格:{e}")
输出:
TEXT
📖 仅展示
# 函数定义成功
💡 什么时候用
raise? 当函数遇到了"不应该发生的"情况时——参数值非法、前置条件不满足、依赖的资源不可用。抛出异常让调用方决定怎么处理,而不是在函数内部偷偷吞掉错误。
5. 自定义异常类
你可以创建自己的异常类型,让错误信息更精确:
▶ 示例:自定义异常与银行取款
PYTHON
class BalanceError(Exception):
"""余额不足异常"""
pass
class AccountLockedError(Exception):
"""账户锁定异常"""
pass
def withdraw(balance, amount):
if amount > balance:
raise BalanceError(f"余额不足!需要{amount},当前余额{balance}")
if balance < 0:
raise AccountLockedError("账户已锁定")
return balance - amount
try:
withdraw(100, 200)
except BalanceError as e:
print(f"交易失败:{e}")
except AccountLockedError as e:
print(f"交易失败:{e}")
输出:
TEXT
📖 仅展示
# 函数定义成功
💡 自定义异常让你的代码更清晰—— 调用方可以精确处理不同类型的错误。直接继承
Exception 类即可,通常只需要写一个类名,不需要额外的方法。
6. logging:专业的错误记录
用 print() 输出错误信息在简单脚本里还行,但在正式项目中,应该用 logging 模块:
▶ 示例:logging 配置与使用
PYTHON
import logging
# 基本配置
logging.basicConfig(
level=logging.INFO, # 日志级别
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("app.log", encoding="utf-8"), # 写入文件
logging.StreamHandler() # 同时输出到控制台
]
)
# 不同级别的日志
logging.debug("调试信息") # 调试用,一般不输出
logging.info("程序启动成功") # 一般信息
logging.warning("磁盘使用率85%") # 警告
logging.error("数据库连接失败") # 错误
logging.critical("系统崩溃!") # 致命错误
运行结果(同时写入文件和控制台):
TEXT
📖 仅展示
2026-06-23 16:00:00,123 [INFO] 程序启动成功
2026-06-23 16:00:00,124 [WARNING] 磁盘使用率85%
2026-06-23 16:00:00,124 [ERROR] 数据库连接失败
💡 用
logging 代替 print() 的好处: ① 可以同时输出到控制台和文件。② 有级别控制(开发时全开,上线后只显示 WARNING 以上)。③ 自动带时间戳。④ 不需要在发布前删掉一堆 print()。
7. 常见应用场景
- 用户输入验证:用
try-except处理int()转换错误,给用户友好的提示。 - 文件操作:用
try-except处理文件不存在、权限不足等错误。 - 网络请求:处理超时、连接失败、数据解析错误。
- 数据库操作:处理连接失败、SQL 语法错误、唯一约束冲突。
- API 开发:用自定义异常精确区分不同类型的错误。
❓ 常见问题
Q
except Exception as e 中的 as e 是什么意思?A
as e 把捕获到的异常对象赋值给变量 e,这样你就可以查看异常的具体信息——print(e) 显示错误描述,type(e).__name__ 显示异常类型名。不加 as e 也能捕获异常,但看不到详细错误信息。Q 什么时候应该用异常,什么时候应该用 if 判断?
A 可以用 if 预判的错误就用 if(比如检查除数为零)。无法预判的错误用 try-except(比如网络请求、文件操作)。Python 社区的 EAFP 风格倾向用 try——不过这是一个审美问题,两种方式各有适用场景。
Q
finally 中的代码一定会执行吗?A 几乎总是——即使 try 块中有
return、break 或 continue,finally 仍然会执行。唯一的例外是程序被强制终止(如 os._exit() 或系统断电)。这使 finally 成为释放资源的绝佳场所。📖 小节
- 异常是程序运行时发生的错误,用
try-except处理 - 结构:
try→except(按具体异常类型)→else(无异常)→finally(总是执行) - 常见异常:
ValueError、TypeError、FileNotFoundError、ZeroDivisionError、IndexError、KeyError raise主动抛出异常,让调用方处理错误- 自定义异常类继承
Exception,精确区分错误类型 logging模块替代print(),支持级别控制、文件输出、时间戳
📝 作业
-
基础题(难度⭐):写一个程序,让用户输入两个数字,计算它们的除法。用
try-except处理ValueError(输入不是数字)和ZeroDivisionError(除数为零),分别给出友好的错误提示。 -
进阶题(难度⭐⭐):写一个函数
read_int_from_file(filename),读取文件中的第一个数字并返回。用 try-except 处理:文件不存在、文件内容不是有效数字、文件为空等错误情况。每种情况返回不同的错误提示。 -
挑战题(难度⭐⭐⭐):写一个"安全的计算器"程序。用户输入表达式(如
10 / 3),程序计算并输出结果。要求:① 用try-except处理除零、值错误、类型错误。② 用logging记录每次的计算操作(INFO)和错误(ERROR)到calculator.log文件。③ 支持exit退出。提示:用eval()计算表达式(注意安全问题,此练习场景可用)。