让使用 @contextmanager 创建的上下文管理器在异常情况下正常工作

Posted

技术标签:

【中文标题】让使用 @contextmanager 创建的上下文管理器在异常情况下正常工作【英文标题】:Getting a context manager created with @contextmanager to work properly with exceptions 【发布时间】:2019-01-03 20:47:42 【问题描述】:

我有以下代码

from contextlib import contextmanager

@contextmanager
def simple_context_manager():
    print("starting context manager")
    yield
    print("finished context manager")

try:
    with simple_context_manager():
        raise RuntimeError
except RuntimeError:
    print("Caught the error")
print("Moving on")

现在打印出来了

starting context manager
Caught the error
Moving on

这告诉我上下文管理器没有关闭。 如何让它关闭并打印“完成的上下文管理器”行?

由于我使用的是装饰器,我没有专用的__exit__ 函数,我认为应该根据this 调用。

所以我不确定如何让我的上下文管理器在其上下文中发生错误的情况下退出。

【问题讨论】:

【参考方案1】:

你需要一个 try-finally:

@contextmanager
def simple_context_manager():
    print("starting context manager")
    try:
        yield
    finally:
        print("finished context manager")

如果异常从with 语句传播出来,@contextmanager 装饰器会将throw 异常传递到yield 处的装饰生成器中。 finally 让我们不管是否发生异常都执行一个清理块,所以我们使用它。

【讨论】:

问题是:为什么默认没有实现这种行为?我的意思是,with 语句是 Python 中最接近 C++ RAII 的东西,唯一真正重要使用它的情况就是透明的异常处理。当我们不需要在引发异常时调用上下文管理器的退出函数时可能会出现什么情况? @Semisonic:@contextmanager 装饰器的设计者必须选择不同的方式将异常信息传递到生成器(并将异常抑制信息传递到生成器之外)才能正常工作。他们选择的设计可能是在这里传达异常信息最自然的方式,我不知道他们是否意识到有多少人会忘记他们需要finally

以上是关于让使用 @contextmanager 创建的上下文管理器在异常情况下正常工作的主要内容,如果未能解决你的问题,请参考以下文章

Python上下文管理器(Context managers)

Python3标准库:contextlib上下文管理器工具

Python 类型提示和上下文管理器

Python 上下文管理器模块--contextlib

Python上下文管理使用

python上下文管理器