首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >contextmanager的奇怪行为

contextmanager的奇怪行为
EN

Stack Overflow用户
提问于 2022-11-11 00:50:43
回答 1查看 21关注 0票数 1

请看下面的示例:

代码语言:javascript
复制
from contextlib import AbstractContextManager, contextmanager


class MyClass(AbstractContextManager):
    _values = {}

    @contextmanager
    def using(self, name, value):
        print(f'Allocating {name} = {value}')
        self._values[name] = value
        try:
            yield
        finally:
            print(f'Releasing {name}')
            del self._values[name]

    def __enter__(self):
        return self.using('FOO', 42).__enter__()

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


with MyClass():
    print('Doing work...')

我希望上面的代码能打印以下内容:

代码语言:javascript
复制
Allocating FOO = 42
Doing work...
Releasing FOO

相反,这是正在印刷的:

代码语言:javascript
复制
Allocating FOO = 42
Releasing FOO
Doing work...

为什么FOO被急切地释放了?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-11-11 01:02:45

您在这里创建两个上下文管理器。实际上,只有其中一个上下文管理器实现正确。

您的using上下文管理器很好,但是您也在MyClass上实现了上下文管理器协议,并且MyClass上的实现已经中断。using创建一个using上下文管理器,输入它,返回上下文管理器的__enter__返回的内容,然后将上下文管理器抛出

退出using上下文管理器时不退出MyClass()上下文管理器。你根本就没退出过!将using上下文管理器扔掉。它被回收,当它被回收时,生成器会被自动调用close,作为正常的生成器清理的一部分。这会向生成器抛出一个GeneratorExit异常,从而触发finally块。

Python并不保证何时会进行这种清理(或者实际上,如果会发生这种情况),但是在实践中,当using上下文管理器无法到达时,CPython的引用计数机制就会触发清理。

除此之外,如果_values应该是一个实例变量,则应该将其设置为__init__方法中的self._values = {}。现在,它是一个类变量。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74396909

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档