with 用来管理“进入/退出”型资源(文件、锁、连接等)。它把获取资源与释放资源打包成一个原子过程:无论代码块里是否抛异常,退出时都会自动清理,避免泄漏。
核心概念
• 能被 with 使用的对象叫上下文管理器(context manager),实现了两个方法:
• enter(self):进入时执行,返回值会赋给 as 后的变量。
• exit(self, exc_type, exc, tb):退出时执行;若返回 True,会吞掉异常。
• 语法等价于 try/finally 的糖衣:
mgr = get_manager()
val = mgr.__enter__()
try:
# your code
finally:
mgr.__exit__(*sys.exc_info())
常见用法
1) 文件:用完自动关闭(即便出错)
with open("data.txt", "r", encoding="utf-8") as f:
text = f.read()
2) 线程锁:加锁/解锁成对出现
from threading import Lock
lock = Lock()
with lock:
critical_section()
3) 数据库事务:异常回滚,正常提交
import sqlite3
with sqlite3.connect("db.sqlite3") as conn:
conn.execute("INSERT INTO t VALUES (?)", (1,)) # 出错则回滚
4) 临时修改环境
from decimal import localcontext, Decimal, getcontext
print(getcontext().prec) # 全局精度
with localcontext() as ctx:
ctx.prec = 6
print((Decimal(1) / Decimal(7))) # 在 with 内使用更高/更低精度
异步版
对需要异步清理的资源,用 async with:
例如:aiofiles 或 redis 异步客户端
from redis.asyncio import Redis
async def main():
async with Redis(host="localhost", decode_responses=True) as r:
print(await r.ping()) # True 退出时自动 aclose()
多个上下文与顺序
with A() as a, B() as b:
...
退出顺序是 B -> A(后进先出)
自定义上下文管理器
类方式
class Timer:
import time
def __enter__(self):
from time import perf_counter
self.t0 = perf_counter()
return self
def __exit__(self, exc_type, exc, tb):
from time import perf_counter
self.elapsed = perf_counter() - self.t0
print(f"elapsed: {self.elapsed:.3f}s")
return False # 不吞异常
with Timer():
do_work()
函数方式(contextlib.contextmanager)
from contextlib import contextmanager
@contextmanager
def open_upper(path):
f = open(path, "r", encoding="utf-8")
try:
yield (line.upper() for line in f)
finally:
f.close()
小结
• with = 自动进入/退出 + 异常安全清理。
• 优先用于文件、网络/数据库连接、锁、临时配置等需要成对清理的场景。
• 异步资源用 async with(例如你上条提到的 Redis 异步客户端,async with Redis(…) 退出时会自动 aclose())。