1. 先搞懂:with 语句为啥能“自动管资源”?🤔
之前用
with open() 时,文件会自动关闭——这不是“魔法”,是因为文件对象遵守了 上下文管理协议 :只要一个类实现了__enter__ 和__exit__两个方法,它的对象就能用在 with 语句里:
- ✨
__enter__:with语句开始时执行(比如“打开文件”“连接数据库”),返回的对象会赋值给as后的变量; - 🧹
__exit__:with语句结束时执行(不管代码是否出错),负责“清理资源”(比如“关闭文件”“断开数据库连接”)。
类比:with像“酒店前台”——__enter__是“帮你开房门、递房卡”,__exit__是“你退房时自动收房卡、关水电”,不用你自己操心!
2. 动手写:数据库连接的上下文管理器——自动连 / 断 🗄️
关键字:自定义上下文管理器、数据库资源管理
内容详解:
结合第 7 章的数据库操作,写一个“自动管理数据库连接”的上下文管理器类:
import mysql.connector
# 定义遵守上下文管理协议的类
class DBContextManager:
def __init__(self, host, user, password, database):
# 初始化数据库配置(状态)self.db_config = {
"host": host,
"user": user,
"password": password,
"database": database
}
self.conn = None # 保存数据库连接
# with 开始时:连接数据库
def __enter__(self):
self.conn = mysql.connector.connect(**self.db_config)
return self.conn # 返回连接对象给 with 的 as 变量
# with 结束时:关闭连接(不管是否出错)def __exit__(self, exc_type, exc_val, exc_tb):
if self.conn:
self.conn.close()
# 处理异常(可选):返回 True 表示异常已处理,False 则抛出
return False
用起来超省心——不用手动关连接:
# 使用上下文管理器:自动连接、自动关闭
with DBContextManager(
host="localhost",
user="root",
password="你的密码",
database="web_log_db"
) as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM access_log")
count = cursor.fetchone()[0]
print(f"总访问次数:{count}")
# 代码块结束后,conn 已经自动关闭!
哪怕代码块里抛异常(比如 SQL 写错了),__exit__也会执行,保证连接被关闭——彻底解决“忘记关资源”的问题!
3. 落地 Web 应用:用上下文管理器简化日志代码 🕸️
关键字:Web 应用集成、代码简化
内容详解:
把上下文管理器用到 Flask Web 应用中,简化“写数据库日志”的代码(对比第 7 章的代码):
from flask import Flask, request
import datetime
app = Flask(__name__)
# 复用上面的 DBContextManager 类
class DBContextManager:
# ...(和前面的定义一样)# 写日志的视图函数:用上下文管理器简化
@app.route("/")
def home():
now = datetime.datetime.now()
user_ip = request.remote_addr
action = "访问首页"
# 用 with 自动管理数据库连接
with DBContextManager(
host="localhost",
user="root",
password="你的密码",
database="web_log_db"
) as conn:
cursor = conn.cursor()
cursor.execute("INSERT INTO access_log (log_time, user_ip, action) VALUES (%s, %s, %s)",
(now, user_ip, action)
)
conn.commit()
return "首页(日志已存,连接已自动关闭)"
if __name__ == "__main__":
app.run(debug=True)
对比之前的代码:不用写“连接→关闭”的重复逻辑,代码更短、更安全——这就是上下文管理协议的价值!
4. 进阶技巧:在__exit__里处理异常 🚨
关键字:__exit__参数、异常处理
内容详解:
__exit__有三个参数:exc_type(异常类型)、exc_val(异常值)、exc_tb(异常追踪栈)——可以用它们在 with 结束时处理异常:
class SafeDBContextManager(DBContextManager):
def __exit__(self, exc_type, exc_val, exc_tb):
# 先执行父类的关闭逻辑
super().__exit__(exc_type, exc_val, exc_tb)
# 如果有异常,打印日志(不抛出)if exc_type:
print(f"数据库操作出错:{exc_val}")
return True # 返回 True 表示“异常已处理,不用向上抛”return False
用这个类时,即使代码块里出错,也只会打印日志,不会让 Web 应用崩溃——适合生产环境的“容错处理”!
5. 划重点:上下文管理协议的“灵魂”🎯
关键字:资源安全、代码简洁、可复用
内容详解:
上下文管理协议解决了两个核心问题:
- 🔒 资源安全:不管代码是否正常执行 / 抛异常,资源都会被清理(不会出现“数据库连接泄露”);
- 📝 代码简洁:把“打开 / 关闭资源”的重复逻辑封装,业务代码更聚焦核心功能;
- 🧩 可复用:写好的上下文管理器可以在多个项目里直接用(比如文件、数据库、网络连接等场景)。
这也是 Python“优雅编程”的体现——用协议规范行为,让代码既安全又简洁!
正文完