国庆节10月1日
--


Python with 关键字的使用方法和上下文管理器

在 Python 编程中,资源管理是一个重要但容易被忽视的环节。 with 关键字为我们提供了一种优雅的方式来处理文件操作、数据库连接等需要明确释放资源的场景。
with 是 Python 中的一个关键字,用于上下文管理协议(Context Management Protocol)。它简化了资源管理代码,特别是那些需要明确释放或清理的资源(如文件、网络连接、数据库连接等)。
我们先看一个典型的文件操作示例:
1file = open('example.txt', 'r')2try:3 content = file.read()4 # 处理文件内容5finally:6 file.close()这种写法存在几个问题:
try-finally 块,可能会忘记调用 close()with 语句通过上下文管理协议(Context Management Protocol)解决了这些问题:
with 语句的基本形式如下:
1with expression [as variable]:2 # 代码块expression 返回一个支持上下文管理协议的对象as variable 是可选的,用于将表达式结果赋值给变量最常见的 with 语句应用是文件操作:
1with open('example.txt', 'r') as file:2 content = file.read()3 print(content)4# 文件已自动关闭这段代码等价于前面的 try-finally 实现,但更加简洁明了。
with 语句背后是 Python 的上下文管理协议,该协议要求对象实现两个方法:
__enter__() :进入上下文时调用,返回值赋给 as 后的变量__exit__() :退出上下文时调用,处理清理工作
__exit__() 方法接收三个参数:
exc_type :异常类型exc_val :异常值exc_tb :异常追踪信息如果 __exit__() 返回 True ,则表示异常已被处理,不会继续传播;返回 False 或 None ,异常会继续向外传播。
1# 同时打开多个文件2with open('input.txt', 'r') as infile, open('output.txt', 'w') as outfile:3 content = infile.read()4 outfile.write(content.upper())1import sqlite32
3with sqlite3.connect('database.db') as conn:4 cursor = conn.cursor()5 cursor.execute('SELECT * FROM users')6 results = cursor.fetchall()7# 连接自动关闭1import threading2
3lock = threading.Lock()4
5with lock:6 # 临界区代码7 print("这段代码是线程安全的")1import decimal2
3with decimal.localcontext() as ctx:4 ctx.prec = 42 # 临时设置高精度5 # 执行高精度计算6 # 精度恢复原设置我们可以通过实现 __enter__ 和 __exit__ 方法创建自定义的上下文管理器:
1class Timer:2 def __enter__(self):3 import time4 self.start = time.time()5 return self6
7 def __exit__(self, exc_type, exc_val, exc_tb):8 import time9 self.end = time.time()10 print(f"耗时: {self.end - self.start:.2f}秒")11 return False12
13# 使用示例14with Timer() as t:15 # 执行一些耗时操作16 sum(range(1000000))Python 的 contextlib 模块提供了更简单的方式来创建上下文管理器:
1from contextlib import contextmanager2
3@contextmanager4def tag(name):5 print(f"<{name}>")6 yield7 print(f"</{name}>")8
9# 使用示例10with tag("h1"):11 print("这是一个标题")输出:
1<h1>2这是一个标题3</h1>1、错误地认为 with 只能用于文件 :
1# 错误:认为只有文件需要with2conn = sqlite3.connect('db.sqlite')3# 应该使用with语句2、忽略__exit__的返回值 :
1class MyContext:2 def __exit__(self, exc_type, exc_val, exc_tb):3 # 忘记返回True/False可能导致异常处理不符合预期4 passwith 语句with 块中的代码应该只包含与资源相关的操作with 语句中管理多个资源| 关键点 | 说明 |
|---|---|
| 自动资源管理 | with 语句确保资源被正确释放 |
| 上下文协议 | 需要实现 __enter__ 和 __exit__ 方法 |
| 异常安全 | 即使代码块中出现异常,资源也会被释放 |
| 常见应用 | 文件操作、数据库连接、线程锁等 |
| 自定义实现 | 可以通过类或 contextlib 创建自定义上下文管理器 |
with 语句是 Python 中一项强大的特性,它不仅能简化代码,还能提高程序的健壮性。掌握 with 语句的使用和原理,将帮助你写出更专业、更可靠的 Python 代码。
with 语句用来做上下文管理:代码块执行前后自动完成”准备/清理”动作,最典型的就是文件用完自动关闭with 表达式 as 变量: 加缩进的代码块;as 变量 可以省略(如直接 with lock:)__enter__()(进入时调用,返回值赋给 as 后的变量)和 __exit__()(退出时调用,负责清理)__exit__() 接收三个参数:exc_type(异常类型)、exc_val(异常值)、exc_tb(追踪信息);它返回 True 表示异常已被处理、不再往外传,返回 False 或 None 则异常继续向外传播with open(文件名, 模式) as f:——写入用 'w' 模式、读取用 'r' 模式,出了 with 代码块文件就已自动关闭with 可以同时管理多个资源,用逗号隔开:with open('input.txt', 'r') as infile, open('output.txt', 'w') as outfile:sqlite3.connect())、线程锁(threading.Lock())、临时修改系统状态(decimal.localcontext())__enter__ / __exit__;或者用 contextlib 的 @contextmanager 装饰一个带 yield 的函数(yield 前是准备、yield 后是清理)True/False;管资源优先用 with,且 with 块里只放与资源相关的操作 2-1 with 语句基本用法
创建文件 test_with.py,完成以下操作:
with 语句读取文件内容with 语句写入文件批改(2026-09-28):✅ 正确(先写入
text.txt再读出,输出Hello Python!,文件内容与写入值一致)。
一级 · 思路:两段独立的 with:一段负责写、一段负责读;每段结束文件都会自动关掉,不用手动关
二级 · 方法:with open('文件名', 'w') as f: 写入用 f.write("内容");with open('文件名', 'r') as f: 读取用 f.read()
三级 · 骨架:with open('test.txt', '____') as f: / f.____("Hello, Python!") / content = f.____()
1# 2-12# 写入文件3with open('test.txt', 'w') as f:4 f.write("Hello, Python!")5
6# 读取文件7with open('test.txt', 'r') as f:8 content = f.read()9 print(content) 2-2 同时操作多个文件
创建文件 test_multi.py,完成以下操作:
with 同时打开两个文件批改(2026-09-28):⚠️ 复制功能正常(
dest.txt内容为你好),但有调试残留print(f2),把文件对象打成了<_io.TextIOWrapper name='dest.txt' ...>;这一行应删掉(或改成print("复制完成"))。
一级 · 思路:一个 with 里用逗号挂两个文件对象,一个只读、一个只写;先从读的对象取内容,再交给写的对象
二级 · 方法:with open('源', 'r') as infile, open('目标', 'w') as outfile:;读 infile.read()、写 outfile.write(内容)
三级 · 骨架:with open('source.txt', 'r') as ____, open('dest.txt', 'w') as ____: / content = infile.____() / outfile.____(content)
1# 2-22# 先创建源文件3with open('source.txt', 'w') as f:4 f.write("这是源文件内容")5
6# 同时读取和写入7with open('source.txt', 'r') as infile, open('dest.txt', 'w') as outfile:8 content = infile.read()9 outfile.write(content.upper())10 print("复制完成") 2-3 自定义上下文管理器
创建文件 test_custom.py,完成以下操作:
一级 · 思路:类就像个”开关”:进入时记下开始时间,退出时再记一次、把差值打印出来
二级 · 方法:类里实现 __enter__(self)(进入时调用,记录开始时间并 return self)和 __exit__(self, exc_type, exc_val, exc_tb)(退出时调用,算耗时并打印、return False);取时间用 time.time()
三级 · 骨架:class Timer: / def ____(self): / self.start = time.time() / return ____ / def ____(self, exc_type, exc_val, exc_tb): / print(f"耗时: {self.end - self.start:.2f}秒")
1# 2-32class Timer:3 def __enter__(self):4 import time5 self.start = time.time()6 return self7
8 def __exit__(self, exc_type, exc_val, exc_tb):9 import time10 self.end = time.time()11 print(f"耗时: {self.end - self.start:.2f}秒")12 return False13
14# 使用示例15with Timer():16 total = sum(range(1000000))17 print(f"计算结果: {total}") 2-4 contextlib 模块
创建文件 test_contextlib.py,完成以下操作:
一级 · 思路:这次不写类,改成写一个函数:yield 之前的代码在进入时执行(打印开标签),yield 之后的代码在退出时执行(打印闭标签)
二级 · 方法:from contextlib import contextmanager 导入装饰器,用 @contextmanager 装饰带 yield 的函数
三级 · 骨架:from contextlib import ____ / @____ / def tag(name): / print(f"<{name}>") / ____ / print(f"</{name}>")
1# 2-42from contextlib import contextmanager3
4@contextmanager5def tag(name):6 print(f"<{name}>")7 yield8 print(f"</{name}>")9
10# 使用示例11with tag("h1"):12 print("这是一个标题")13
14with tag("p"):15 print("这是一个段落")如果你喜欢,那么欢迎来到我的世界!
了解更多暂未播放



"生活不是等待暴风雨过去,而是学会在雨中跳舞。"—— 塞维涅夫人


