一、核心作用
- 自动执行「进入操作」(如打开文件、创建连接)和「退出操作」(如关闭文件、释放连接)
- 即使代码块中发生异常,也能保证退出操作被执行(相当于自带
try-finally但更简洁)
二、 基本语法结构
python
with 上下文表达式 as 变量名:
# 代码块(使用资源的逻辑)
# 代码块结束后,自动执行退出操作(无需手动关闭)
- 「上下文表达式」:返回一个上下文管理器对象 (如
open()、自定义的对象实例) - 变量名」:可选,接收上下文管理器的
__enter__()方法返回的值(不是管理器对象本身
三、工作原理
一个对象能被with使用,必须实现两个特殊方法
__enter__(self):进入with代码块时执行。- 负责初始化资源(如打开文件、连接数据库)
- 返回的值会赋值给
as后的变量名
__exit__(self, exc_type, exc_val, exc_tb):退出with代码块时执行(无论正常结束还是异常结束)- 负责释放资源(如关闭文件、断开连接)
- 接收异常信息,可返回
True表示忽略异常。
示例:自定义简单上下文管理器
python
class MyFileReader:
def __init__(self, file_path):
self.file_path = file_path
self.file = None # 进入with代码块时执行
def __enter__(self):
self.file = open(self.file_path, 'r', encoding='utf-8')
return self.file # 返回文件对象,赋值给as后的变量 # 退出with代码块时执行
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close() # 自动关闭文件
# 若返回True,会忽略代码块中的异常;返回None/False则会抛出异常
return False
# 使用 with
MyFileReader('test.txt') as f:
content = f.read() # 直接使用__enter__返回的文件对象
# 代码块结束后,自动调用__exit__关闭文件
四、常见使用场景
1.操作文件
替代手动open()/close(),避免忘记关闭文件
python
# 正确用法:自动关闭文件
with open('test.txt', 'r', encoding='utf-8') as f:
content = f.read()
# 等价于(但更简洁)
f = open('test.txt', 'r', encoding='utf-8')
try:
content = f.read()
finally:
f.close()
2.使用自定义工具类
只有实现了 __enter__ 和 __exit__,才能直接用 with
3.多资源同时管理
可在一个 with 中管理多个资源(用逗号分隔),按顺序进入、逆序退出:
python
# 同时打开两个文件,自动关闭
with open('a.txt', 'r') as f1, open('b.txt', 'w') as f2:
content = f1.read()
f2.write(content)
4. 数据库连接/网络连接
自动释放连接,避免连接池耗尽:
python
import pymysql
with pymysql.connect(host='localhost', user='root', password='123456', db='test') as conn:
with conn.cursor() as cursor:
cursor.execute('SELECT * FROM users')
result = cursor.fetchall()
# 自动关闭cursor和conn
五、关键注意事项
as后的变量是__enter__的返回值,不是上下文管理器对象:- 如
open()的__enter__返回文件对象,所以as f中f是文件对象,不是open()的返回值 - 你的
MyFileReader中__enter__返回self,所以reader是MyFileReader实例,可调用其方法。
- 如
- 异常处理
- 代码块中发生异常时,
__exit__仍会执行(保证资源释放) - 若
__exit__返回True,异常会被静默处理(不抛出);返回False或None,异常会正常抛出
- 代码块中发生异常时,
- 并非所有对象都能用于
with- 必须实现
__enter__和__exit__方法(即「上下文管理器协议」) - 常见支持的对象:
open()、threading.Lock、pymysql.connect、自定义实现协议的类
- 必须实现
- 适用于「需要成对操作」的场景
- 打开 / 关闭、连接 / 断开、加锁 / 解锁等,用
with能简化代码并避免遗漏
- 打开 / 关闭、连接 / 断开、加锁 / 解锁等,用
六、进阶:用contextlib简化自定义上下文管理器
如果不想手动实现 __enter__ 和 __exit__,可使用 contextlib.contextmanager 装饰器(基于生成器):
python
from contextlib import contextmanager
@contextmanager
def my_file_reader(file_path):
# 相当于__enter__的逻辑
f = open(file_path, 'r', encoding='utf-8')
yield f # 返回值赋值给as后的变量,暂停在这里执行代码块
# 代码块结束后,执行以下(相当于__exit__的逻辑)
f.close()
# 使用方式不变
with my_file_reader('test.txt') as f:
content = f.read()