Python——contextlib包的使用


整体说明

  • contextlib 模块是 Python 标准库的一部分,提供了一系列简化上下文管理器实现的实用工具

@contextmanager 装饰器

  • @contextmanager 允许将生成器函数转换为上下文管理器,无需显式定义类

  • @contextmanager 核心逻辑通过 yield 分割为“进入上下文”和“退出上下文”两部分

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from contextlib import contextmanager

    @contextmanager
    def file_manager(file_path, mode):
    file = open(file_path, mode)
    yield file
    file.close()

    with file_manager('test.txt', 'w') as f:
    f.write('Hello, contextlib!')
    • 在这个例子中,yield 之前的代码 file = open(file_path, mode) 用于获取资源,yield 之后的代码 file.close() 用于释放资源
    • 注:yield 之后的语句在第二次调用函数时被调用(第一次调用函数返回 yield 的结果)

closing

  • 对于只提供 close() 方法但未实现上下文协议的对象,closing 可确保其 close() 被调用

    1
    2
    3
    4
    5
    from contextlib import closing
    import urllib.request

    with closing(urllib.request.urlopen('https://www.example.com')) as response:
    html = response.read()
    • 上述代码等价于手动使用try-finally块来调用close()方法,但closing类使代码更加简洁

suppress上下文管理器

  • 在需要忽略某些非关键异常时,suppress可替代繁琐的try-except

    1
    2
    3
    4
    5
    6
    from contextlib import suppress

    with suppress(FileNotFoundError):
    with open('nonexistent_file.txt', 'r') as f:
    content = f.read()
    print("程序继续执行,不抛出异常")
  • suppress还可以同时抑制多个异常,如with suppress(FileNotFoundError, PermissionError):


nullcontext上下文管理器

  • 当代码需要根据条件决定是否使用上下文管理器时,nullcontext提供“空上下文”,避免重复代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from contextlib import nullcontext

    def process_data(data, use_file=True):
    context = open('output.txt', 'w') if use_file else nullcontext()
    with context as f:
    if f:
    f.write(str(data))
    else:
    print(f"直接输出:{data}")

    process_data("临时数据", use_file=False)
    process_data("重要数据", use_file=True)

ExitStack

  • contextlib 支持通过 with 语句嵌套或使用 ExitStack 批量管理多个上下文,尤其适合动态生成的资源列表

    1
    2
    3
    4
    5
    6
    7
    from contextlib import ExitStack

    file_names = ('file1.txt', 'file2.txt', 'file3.txt')
    with ExitStack() as stack:
    files = [stack.enter_context(open(name, 'w')) for name in file_names]
    for f in files:
    f.write('示例内容')
    • 在这个例子中,ExitStack 会自动管理多个文件的上下文,确保在退出 with 块时所有文件都被正确关闭。