Python with 语句的用法详解
with 语句用于上下文管理,它能自动处理资源的获取和释放,确保即使发生异常也能正确清理(如关闭文件、释放锁、恢复状态等)。常见应用场景包括文件操作、数据库连接、线程锁、临时状态修改等。
1. 基本语法
with 上下文管理器 [as 变量]:
代码块
- 上下文管理器:支持上下文管理协议的对象(实现了
__enter__和__exit__方法)。 as 变量(可选):将__enter__的返回值赋给变量,供代码块使用。- 执行流程:
- 调用
上下文管理器.__enter__(),返回值赋给变量(如果有as)。 - 执行代码块。
- 无论代码块是否发生异常,都会调用
上下文管理器.__exit__()进行清理。
- 调用
2. 最常见用法:文件操作
# 传统方式(需要手动关闭)
f = open('test.txt', 'r')
try:
content = f.read()
finally:
f.close()
# with 方式(自动关闭)
with open('test.txt', 'r') as f:
content = f.read()
# 离开 with 块后,f 已被自动关闭
优点:无需显式 try/finally,代码更简洁,不会忘记释放资源。
3. 其他常见应用
(1) 线程锁(threading.Lock)
import threading
lock = threading.Lock()
with lock:
# 自动获取锁,退出时自动释放
critical_section()
(2) 数据库连接(如 sqlite3)
import sqlite3
with sqlite3.connect('db.sqlite') as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM users')
# 退出时自动提交或回滚(取决于实现)
(3) 临时修改上下文(如 decimal 上下文)
from decimal import Decimal, getcontext, localcontext
with localcontext() as ctx:
ctx.prec = 2
print(Decimal('1.234') + Decimal('2.345')) # 使用临时精度
# 退出后恢复原精度
(4) 重定向标准输出(如 contextlib.redirect_stdout)
import io
from contextlib import redirect_stdout
f = io.StringIO()
with redirect_stdout(f):
print("Hello") # 输出被捕获到 f
output = f.getvalue()
4. 同时管理多个上下文
# 同时打开两个文件
with open('input.txt') as fin, open('output.txt', 'w') as fout:
fout.write(fin.read())
5. 自定义上下文管理器
有两种方式:类(实现 __enter__ 和 __exit__)或 contextlib.contextmanager 装饰器。
方式一:定义类
class ManagedFile:
def __init__(self, filename, mode='r'):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
# 返回 True 表示异常已被处理,不再向外抛出
return False
with ManagedFile('test.txt', 'w') as f:
f.write('Hello')
方式二:使用 @contextmanager 装饰器(生成器)
from contextlib import contextmanager
@contextmanager
def managed_file(filename, mode='r'):
f = open(filename, mode)
try:
yield f # 进入 with 块时返回,退出时继续执行
finally:
f.close()
with managed_file('test.txt', 'r') as f:
content = f.read()
6. 工作原理
with 语句相当于以下 try/finally 结构:
manager = 上下文管理器
var = manager.__enter__()
try:
代码块
finally:
manager.__exit__(exc_type, exc_val, exc_tb)
__exit__的三个参数表示代码块中发生的异常(无异常则为None, None, None)。- 如果
__exit__返回True,异常会被抑制(不再向上抛出);返回False则继续抛出。
7. 总结
with确保资源正确释放,推荐用于任何需要成对操作(打开/关闭、获取/释放)的场景。- 常用的内置上下文管理器:文件、锁、
decimal.localcontext、redirect_stdout等。 - 自定义上下文管理器有两种简单方式:类或
@contextmanager生成器。 - 多个上下文可以写在一个
with语句中,用逗号分隔。