Python 语法精炼10
上下文管理器
在 Python 中,上下文管理器是一种对象,它定义了程序在进入和退出某个运行环境时的行为。
简单说:
“上下文管理器可以在一段代码执行前后自动执行特定操作。”
最常见的例子:
1 | with open('data.txt', 'r') as f: |
这段代码的行为是:
- 进入上下文:调用
open()→ 创建文件对象。 - 执行代码块:
with缩进内的语句。 - 退出上下文:自动调用
f.close(),即使中途发生异常也会关闭文件。
工作原理
语法结构
1 | with 表达式 [as 变量]: |
工作流程
-
执行
表达式,获取一个上下文管理器对象。 -
调用该对象的
__enter__()方法。- 如果有
as 变量,将__enter__()的返回值赋给变量。
- 如果有
-
执行
with代码块中的语句。 -
当代码块执行完毕(无论是否发生异常):
- 调用对象的
__exit__(exc_type, exc_value, traceback)。 __exit__决定是否“吞掉”异常(返回 True 表示忽略异常,False 表示继续抛出)。
- 调用对象的
常见示例
传统写法:
1 | f = open('data.txt', 'r') |
with 写法:
1 | with open('data.txt', 'r') as f: |
✅ 优点:
- 自动关闭文件。
- 即使读取中发生异常,也能安全释放资源。
- 更简洁、更安全。
核心机制
一个对象要能用于 with,必须实现这两个方法:
| 方法 | 调用时机 | 作用 |
|---|---|---|
__enter__(self) |
进入 with 块前调用 |
返回要绑定的对象 |
__exit__(self, exc_type, exc_val, exc_tb) |
离开 with 块时调用 |
清理操作 |
示例:
1 | class MyContext: |
输出:
1 | 进入上下文 |
注意:异常被吞掉了,没有抛出
多个上下文并列写法,Python 3.1+ 加入此方法
1 | with open('a.txt') as fa, open('b.txt') as fb: |
与嵌套写法等价:
1 | with open('a.txt') as fa: |
应用场景
| 场景 | 示例 | 自动管理内容 |
|---|---|---|
| 文件读写 | with open(...) as f: |
文件句柄关闭 |
| 线程锁 | with lock: |
加锁 / 解锁 |
| 数据库事务 | with connection: |
提交或回滚 |
| 网络连接 | with socket: |
关闭连接 |
| 临时修改状态 | with decimal.localcontext(): |
精度恢复 |
| 压缩包 / tarfile | with zipfile.ZipFile() as z: |
文件关闭 |
contextlib 模块
装饰器 @contextmanager
如果你不想定义类、实现 __enter__/__exit__,
Python 提供了 contextlib.contextmanager 装饰器,可以让你用函数快速创建上下文管理器。
示例:
1 | from contextlib import contextmanager |
输出:
1 | 获取资源 |
✅ 注意:
yield之前的代码相当于__enter__;yield之后的代码相当于__exit__;- 如果
with内部发生异常,会在yield处重新抛出,可在try...finally中清理。
装饰器 @contextmanager 处理异常
1 |
|
输出:
1 | 进入 |
contextlib 常用辅助工具
| 工具 | 说明 | 示例 |
|---|---|---|
contextmanager |
函数快速创建上下文 | 上例 |
closing() |
确保对象有 .close() 方法时自动关闭 |
with closing(urlopen(url)) as page: |
suppress() |
忽略特定异常 | with suppress(FileNotFoundError): os.remove('tmp.txt') |
ExitStack |
动态管理多个上下文 | 动态打开多个文件 |
示例:suppress
1 | from contextlib import suppress |
示例:ExitStack
当需要动态创建多个上下文(数量在运行时才确定):
1 | from contextlib import ExitStack |
最佳实践
- 实现
__enter__/__exit__,或者使用@contextmanager。 - 始终在
__exit__/finally中释放资源。 - 若资源必须关闭,最好配合异常处理,避免资源泄漏。
- 返回值尽量简单,常为自身或资源对象。
- 如果要吞掉异常,请确保你真的希望忽略它(一般返回 False)。
思维导图
1 | with 上下文管理器 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Telason!
