如何从 ExitStack 中删除上下文管理器
Posted
技术标签:
【中文标题】如何从 ExitStack 中删除上下文管理器【英文标题】:How to remove a context manager from an ExitStack 【发布时间】:2016-10-03 01:53:38 【问题描述】:我有一个名为 Updater 的长期运行进程,它向它提交了更新(到 ETL 系统)。更新具有通过将上下文管理器添加到更新程序的 ExitStack 来管理的资源需求。一些更新将包括新配置,这意味着必须从堆栈中释放受影响的资源,并且将添加新配置的资源版本。我需要类似的东西:
with ExitStack() as stack:
ctx_manager = open("file.txt")
f = stack.enter_context(ctx_manager)
...
ctx_pop(ctx_manager, stack) # remove the given context manager from the stack
下面是我已经开始工作的一个例子,但它依赖于访问受保护的成员。我希望可能有比这更“肮脏”的解决方案:
def ctx_pop(cm, stack):
for item in stack._exit_callbacks:
if item.__self__ is cm:
break
else:
raise KeyError(repr(cm))
stack._exit_callbacks.remove(item)
item(None, None, None)
编辑:添加已知解决方案
【问题讨论】:
查看源代码ExitStack
使用 deque
存储上下文 .__exit__
方法的包装器,因此您需要能够通过原始上下文管理器识别包装器,如据我所知,是不可能的。您可能需要重新发明(至少部分)ExitStack
的功能,以便能够提前删除上下文。
@TadhgMcDonald-Jensen 谢谢。我在 Jupyter Notebook 中检查 ExitStack
对象时发现了同样的事情。他们有一个闭包双端队列,__self__
属性是一个上下文管理器。我会把一些代码作为一个已知的解决方案,但我希望有一个不那么“黑客”的解决方案。在我提交给 Python Ideas 或自己添加之前,我想看看是否有更清洁的解决方案(我以前从未这样做过)。
【参考方案1】:
你必须用你自己的pop
-方法扩展ExitStack
:
from contextlib import ExitStack
from collections import deque
class ExitStackWithPop(ExitStack):
def pop(self, cm):
callbacks = self._exit_callbacks
self._exit_callbacks = deque()
found = None
while callbacks:
cb = callbacks.popleft()
if cb.__self__ == cm:
found = cb
else:
self._exit_callbacks.append(cb)
if not found:
raise KeyError("context manager not found")
found(None, None, None)
【讨论】:
它不是_ExitStack__self__
,它只是__self__
,带有两个下划线的属性不是私有的(想想所有神奇的方法名称)你也不需要重新创建@987654328 @,您可以遍历项目和.remove
具有.__self__ == cm
的项目
谢谢!这与我现在使用的解决方案非常接近,但我喜欢子类化的想法。这样看起来更干净。
嘘。您将在此处了解实施细节。
因为this changes 在contextlib 它必须是if cb[1].__self__ == cm:
才能工作。 (无论如何它对 contextlib 的依赖非常低)
奇怪的是,ExitStack 中不包含此功能。它真的应该。显然,有些用例需要动态管理对象。您是否考虑过提交增强请求?【参考方案2】:
contextlib.ExitStack
只支持一次退出所有上下文管理器。您不能单独弹出上下文管理器。如果你想这样做,你应该使用 ExitStack 以外的东西来跟踪你的上下文管理器。
【讨论】:
以上是关于如何从 ExitStack 中删除上下文管理器的主要内容,如果未能解决你的问题,请参考以下文章
如何从“右键单击”上下文菜单(Windows 资源管理器)中选择多个文件以启动同一个应用程序