gpt4 book ai didi

python - 您如何判断上下文管理器是可重用的还是可重入的?

转载 作者:太空狗 更新时间:2023-10-29 20:56:10 25 4
gpt4 key购买 nike

Python contextlib 文档指出上下文管理器可以是一次性的、可重用的或可重入的。可重入的可以在多个 with 语句中使用,包括嵌套的;可重用但不可重入的可以在多个 with 语句中使用但不能嵌套。提到了几个例子。

https://docs.python.org/3/library/contextlib.html#reentrant-context-managers

不过,其他上下文管理器的文档并不总是提及它们是什么。例如,unittest.mock 中的 patch 上下文管理器的文档根本没有提到这一点。

一般来说,您会在源代码中查看什么来确定上下文管理器是一次性使用、可重用还是可重入?

最佳答案

一个好方法是查看返回/修改的对象或由 __enter__ 调用设置的上下文,然后查看 __exit__< 中该对象/上下文发生了什么 调用。通常,了解每个对象中的状态实际上是如何改变的会让您清楚嵌套或重新使用该对象时会发生什么。

例如,当您with open("somefile") as f: 时,您将获得一个文件句柄。在 __exit__ 中,您将关闭该文件句柄。当然,关闭文件句柄对象后重新打开它是没有意义的,打开已经打开的文件句柄也没有意义。当然,关闭内部文件句柄也会关闭外部文件句柄,这会产生问题。这就是为什么没有人这样做的原因:

f = open("file.txt")
with f:
# stuff
# File will get closed here

关闭后使用f没有意义,所以我们总是使用:

with open("file.txt") as f:

threading.Lockthreading.RLock 对象也可以用作上下文管理器。这样做是有道理的:

l = threading.Lock()
with l: # This acquires the lock
# stuff
# Lock got released

with l: # Acquired again
# more stuff
# Released again

这不会,因为如果你试图递归地使用 Lock 会死锁

l = threading.Lock()
with l:
# stuff
with l: # Uh-oh, we tried acquiring an already acquired Lock. We'll deadlock here.

但这将适用于 RLock(),它可以递归获取。

stdlib 的另一个示例:multiprocessing.Pool() 可用作 Python 3.3+ 中的上下文管理器。文档是这样说的:

Pool objects now support the context manager protocol. __enter__() returns the pool object, and __exit__() calls terminate().

terminate() 说它是这样做的:

Stops the worker processes immediately without completing outstanding work. When the pool object is garbage collected terminate() will be called immediately.

显然,这是一次性使用。

patch 上下文管理器临时修补一些对象,然后在完成后撤消修补。嵌套在那里绝对没有意义——你为什么要重新修补已经修补过的东西?但是,打补丁、取消打补丁然后再次打补丁在逻辑上确实有意义,因此它应该是可重用的(测试表明是这种情况)。

我不认为真的有任何明确定义的东西可以说“看看这个,你就会知道上下文管理器是可重用/可重入/一次性的”,因为上下文管理器可以字面上做任何。您能做的最好的事情就是了解在 __enter__ 中建立什么上下文,如何分解 __exit__,然后从逻辑上确定重用/重新进入的含义那个上下文是。

关于python - 您如何判断上下文管理器是可重用的还是可重入的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25045379/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com