1,with是什么:
with 语句是 Python 提供的一种上下文管理器(Context Manager) 语法,用于自动管理资源的获取和释放。它可以确保某些"清理"操作(如关闭文件、释放锁、断开连接等)无论代码是否发生异常,都一定会被执行。
为什么要使用with?
举个例子,打开一个文件写入内容通常语下面三种做法:
最简单的方式:
# ❌ 不使用 with:需要手动关闭,且异常时可能忘记关闭f = open('test.txt', 'r')data = f.read()# 如果这里抛出异常,f.close() 就不会执行 → 资源泄漏f.close()
使用try/finally:
# ✅ 使用 try/finally:可以保证关闭,但代码冗长f = open('test.txt', 'r')try: data = f.read()finally: f.close()
使用with时:
# ✅ 使用 with:简洁 + 安全withopen('test.txt', 'r') as f: data = f.read()# 离开 with 块时,f 自动关闭(即使发生异常)
2,with语句背后的工作原理:
with 语句背后依赖两个"魔法方法"(协议方法):
| | |
| | |
| __exit__(self, exc_type, exc_val, exc_tb) | | |
__exit__ 的三个参数
3,动手写with---上下文管理器
例子一:
class MyFile: def __init__(self, filename, mode='r'): self.filename = filename self.mode = mode self.file = None def __enter__(self): print(f"[进入] 打开文件: {self.filename}") self.file = open(self.filename, self.mode, encoding='utf-8') return self.file # 返回值会被赋给 as 后面的变量 def __exit__(self, exc_type, exc_val, exc_tb): print(f"[退出] 关闭文件: {self.filename}") if self.file: self.file.close() # 如果有异常,打印出来,但不抑制(返回 False 或 None) if exc_type is not None: print(f"[异常] 类型={exc_type.__name__}, 信息={exc_val}") return False # 让异常正常向上抛出# 使用with MyFile('demo.txt', 'w') as f: f.write("Hello, with statement!") print("正在写入文件...")
输出为:
[进入] 打开文件: demo.txt正在写入文件...[退出] 关闭文件: demo.txt
例子二:
import timeclass Timer: def __enter__(self): self.start = time.time() return self # 返回自身,方便获取属性 def __exit__(self, exc_type, exc_val, exc_tb): self.elapsed = time.time() - self.start print(f"耗时: {self.elapsed:.4f} 秒")# 使用with Timer() as t: total = sum(i * i for i in range(1_000_000)) print(f"计算结果: {total}")
输出为:
计算结果: 333332833333500000耗时: 0.0821 秒
例子三(项目中常见的使用方式,装饰器实现):
from contextlib import contextmanagerimport time@contextmanagerdef my_timer(): start = time.time() try: yield start # yield 之前 = __enter__ finally: elapsed = time.time() - start print(f"耗时: {elapsed:.4f} 秒") # yield 之后 = __exit__with my_timer() as start_time: print(f"开始时间戳: {start_time}") time.sleep(0.5)
输出为:
开始时间戳: 1785683961.641807耗时: 0.5003 秒
4,with 的高级用法
同时管理多个资源
with open('a.txt') as fa, open('b.txt') as fb: data = fa.read() + fb.read()
常见的内置上下文管理器
| |
| |
| |
| |
| with tempfile.TemporaryDirectory() as d: |
| with contextlib.suppress(FileNotFoundError): |
此处的上下文管理器可以自己看看,后续也会陆续在项目中提到这些。
5、总结
| |
| |
| 实现 __enter__ 和 __exit__ 即可 |
| 简洁、安全、异常安全(exception-safe) |
| 使用 @contextmanager 装饰生成器函数 |
| |
一句话记忆:with = 自动版的 try / finally,让"清理代码"永远不会被忘记执行。