1. 代码的“意外”:异常是躲不开的 🤕
再完美的代码也会遇到“意外”——比如数据库突然连不上、用户输入了非法内容、文件被删了…如果不管这些“意外”,程序会直接崩溃(像汽车撞墙一样)。
异常处理 就是给代码“装安全气囊”:遇到问题时不崩溃,而是“优雅地处理”(比如提示错误、重试操作),让程序更 健壮(不容易挂掉)。
类比:你点外卖,万一商家没货(异常),APP 不会闪退,而是提示“商品售罄”并推荐替代品——这就是“异常处理”的思路!
2. 异常处理入门:try-except——“试试看,出问题就处理”🧪
关键字:try 语句、except 子句、异常捕获
内容详解:
Python 用 try-except 捕获异常,格式是:把“可能出问题的代码”放在 try 里,“出问题后怎么处理”放在 except 里。比如处理“除以 0”的错误:
def divide(a, b):
try:
# 可能出问题的代码:b 为 0 会报错
return a / b
except ZeroDivisionError:
# 捕获“除以 0”异常,处理它
print("错误:除数不能为 0!")
return None # 返回一个安全的结果
# 测试:正常情况
print(divide(4, 2)) # 输出 2.0
# 测试:异常情况
print(divide(4, 0)) # 输出“错误:除数不能为 0!”和 None
没有异常处理时,4/0会让程序崩溃;加了try-except,程序会提示错误并继续运行!
3. 灵活处理:捕获多种异常 +“兜底”处理 🎯
关键字:多 except 子句、Exception 基类、异常信息
内容详解:
一个 try 可以配多个except,捕获不同类型的异常;还能用Exception(所有异常的基类)“兜底”捕获未知异常:
def read_file(file_path):
try:
with open(file_path, "r") as f:
return f.read()
except FileNotFoundError:
# 捕获“文件不存在”异常
print(f"错误:文件 {file_path} 不存在!")
return ""
except PermissionError:
# 捕获“权限不足”异常
print(f"错误:没有读取 {file_path} 的权限!")
return ""
except Exception as e:
# 兜底:捕获其他所有异常,e 是异常对象(包含错误信息)print(f"未知错误:{e}")
return ""
# 测试不同异常
read_file("不存在的文件.txt") # 提示“文件不存在”read_file("/root/ 敏感文件.txt") # 提示“权限不足”read_file(123) # 提示“未知错误:expected str, bytes or os.PathLike object, not int”
这样不管遇到哪种错误,程序都能“温柔地”给出提示,而不是直接崩溃~
4. Web 场景:给数据库操作加“异常防护”🕸️
关键字:Flask 异常处理、数据库异常、重试机制
内容详解:
Web 应用中,数据库连接失败是常见异常——用 try-except 给数据库操作加防护,甚至加“重试”功能:
from flask import Flask
import mysql.connector
import time
app = Flask(__name__)
def get_db_data(retry=3):
while retry > 0:
try:
# 尝试连接数据库
conn = mysql.connector.connect(host="localhost", user="root", password="你的密码", database="web_log_db")
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM access_log")
result = cursor.fetchone()[0]
conn.close()
return result
except mysql.connector.Error as e:
# 捕获数据库异常
print(f"数据库错误:{e},剩余重试次数{retry-1}")
retry -= 1
time.sleep(1) # 等 1 秒再重试
# 重试失败,返回默认值
return "数据库连接失败"
@app.route("/log_count")
def log_count():
count = get_db_data()
return f"总访问次数:{count}"
if __name__ == "__main__":
app.run(debug=True)
现在如果数据库临时断开,程序会重试 3 次;都失败了也会返回“数据库连接失败”,而不是让 Web 应用崩溃——用户体验会好很多!
5. 高阶技巧:自定义异常 +finally“收尾”🧩
关键字:自定义异常、raise 语句、finally 子句
内容详解:
除了 Python 自带的异常,你还能 自定义异常 (处理业务相关的错误);finally 子句 则能保证“不管有没有异常,都执行收尾操作”:
# 自定义异常:业务相关的错误
class InvalidUserError(Exception):
def __init__(self, user_id):
self.user_id = user_id
self.message = f"用户 {user_id} 不存在"
super().__init__(self.message)
def get_user_info(user_id):
# 模拟查询用户,不存在则抛自定义异常
users = {"1": "张三", "2": "李四"}
if user_id not in users:
raise InvalidUserError(user_id) # 主动抛异常
return users[user_id]
def process_user(user_id):
conn = None
try:
user = get_user_info(user_id)
# 模拟数据库操作
conn = mysql.connector.connect(...)
print(f"处理用户{user}")
except InvalidUserError as e:
# 捕获自定义异常
print(f"业务错误:{e.message}")
except Exception as e:
print(f"系统错误:{e}")
finally:
# 不管有没有异常,都关闭数据库连接
if conn:
conn.close()
print("数据库连接已关闭")
# 测试:正常用户
process_user("1") # 输出“处理用户张三”和“数据库连接已关闭”# 测试:不存在的用户
process_user("3") # 输出“业务错误:用户 3 不存在”和“数据库连接已关闭”
自定义异常让“业务错误”和“系统错误”分开处理;finally则保证资源(比如数据库连接)一定会被清理!
6. 避坑指南:异常处理的“红线”🚫
关键字:异常吞没、日志记录、合理捕获
内容详解:
异常处理要避免一个常见错误:吞没异常 (只写except: 却不处理 / 记录错误)——比如:
# 错误示例:吞没异常,出问题了都不知道
try:
4 / 0
except:
pass # 啥也不做
正确的做法是:
- ✅ 只捕获“你能处理的异常”,未知异常交给上层处理;
- ✅ 记录异常信息(比如用
print或日志模块),方便排查问题; - ✅ 不要用异常处理“代替正常的条件判断”(比如用
try判断用户输入是否为数字,不如直接用isdigit())。
记住:异常处理是“应对意外”,不是“掩盖错误”!