首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python 的 with 语句、上下文管理协议

Python 的 with 语句、上下文管理协议

作者头像
YaoQi
发布2026-07-10 19:05:15
发布2026-07-10 19:05:15
10
举报

with 语句是通过上下文管理器协议(Context Manager Protocol)进行资源管理,其核心价值是资源的获取与自动释放。

如果不使用上下文管理器,通常我们可以使用异常机制,在try代码段获取资源,使用资源,在 except、finally 代码段中释放资源。这样在过程中各种异常和失败的情况下都能释放资源。

代码语言:javascript
复制
file = open('some_file.txt', 'w')
try:
    file.write('Hello, World!')
finally:
    file.close() # 确保在任何情况下文件都会被关闭

Python引入了with 语句来简化这种模式:

代码语言:javascript
复制
with open('some_file.txt', 'w') as file:
    file.write('Hello, World!')
# 一旦离开 with 代码块,文件会自动关闭,无需手动调用 file.close()

open() 函数返回的文件对象就是一个上下文管理器。

对象要成为上下文管理器,需要实现两个特殊方法:__enter__() 和 __exit__()。

  1. __enter__():
    • 在进入 with 代码块之前被调用。
    • 它的返回值会赋值给 as 关键字后面的变量(如果使用了 as)。
    • 通常在这里进行资源的分配(如打开文件、获取锁)。
  2. __exit__(exc_type, exc_val, exc_tb):
    • exc_type: 异常的类型(如 ValueError, TypeError)。
    • exc_val: 异常的实例值。
    • exc_tb: 异常的追踪信息(traceback object)。
    • 在离开 with 代码块之后(无论是正常结束还是因异常而结束)被调用。
    • 它负责处理资源的清理工作(如关闭文件、释放锁)。
    • 它有三个参数,用来处理异常:
    • 如果 __exit__() 返回 True,则表示异常已经被处理,with 语句之后的代码会继续执行,就好像什么都没发生一样。
    • 如果 __exit__() 返回 FalseNone(即不返回任何值),异常会继续向上传播。
代码语言:javascript
复制
创建上下文管理器
代码语言:javascript
复制
使用类的方法
代码语言:javascript
复制
import time

class Timer:
    def __enter__(self):
        self.start_time = time.time()
        print("开始计时...")
        return self.start_time # 可以返回自身或其他对象,供 with...as 使用

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end_time = time.time()
        elapsed_time = self.end_time - self.start_time
        print(f"代码块运行耗时: {elapsed_time:.4f} 秒")
        # 返回 False,让任何发生的异常正常抛出
        return False

# 使用自定义的上下文管理器
with Timer() as t:
    print(f"开始时间:{t}")
    # 模拟一些耗时操作
    time.sleep(2)
    # 如果这里发生异常,计时功能依然会正常工作
    # print(1 / 0) # 取消注释测试异常情况

# 输出:
# 开始计时...
# 开始时间:1755850271.0199914
# 代码块运行耗时: 2.0002 秒
代码语言:javascript
复制
代码语言:javascript
复制
使用contextmanager
代码语言:javascript
复制
利用生成器函数(带yield语句的函数)
代码语言:javascript
复制
from contextlib import contextmanager
import time

@contextmanager
def timer():
    start_time = time.time()
    print("开始计时...")
    try:
        yield start_time
        # 在这里 yield 之前的部分相当于 __enter__
    finally:
        # finally 块确保了清理工作一定会执行,相当于 __exit__
        end_time = time.time()
        elapsed_time = end_time - start_time
        print(f"代码块运行耗时: {elapsed_time:.4f} 秒")

# 使用方式完全相同
with timer() as t:
    print(f"开始时间:{t}")
    time.sleep(1.5)

@contextmanager 的工作流程:

  1. 执行 yield 之前的代码(相当于 __enter__)。
  2. yield 的值会返回给 as 子句。
  3. 执行 with 块中的代码。
  4. 无论 with 块中是否发生异常,都会继续执行 yield 之后的代码(在 finally 块中,相当于 __exit__)。

async with

async with 是 with 语句的异步版本,用于管理异步资源。

一个对象要成为异步上下文管理器,需要实现两个特殊的异步方法:__aenter__() 和 __aexit__()。

代码语言:javascript
复制
使用装饰器定义上下文管理器则要使用@asynccontextmanager 
代码语言:javascript
复制

代码语言:javascript
复制
定义相关函数使用async,调用相关函数使用await。
代码语言:javascript
复制

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-09-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 漫跑的小兔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档