Python指出,上下文管理器可以是单一用途、可重用或可重入的。可重入语句可以用于多个with语句,包括嵌套语句;可重用但不能重入的语句可以用于多个with语句,而不是嵌套语句。这里提到了几个例子。
https://docs.python.org/3/library/contextlib.html#reentrant-context-managers
不过,其他上下文管理器的文档并不总是提到它们是什么。例如,patch上下文管理器在unittest.mock中的文档根本没有提到这一点。
通常,您会在源代码中查看哪些内容来确定上下文管理器是单一用途、可重用还是可重入?
发布于 2014-07-30 20:00:30
一种好的方法是查看由__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.Lock和threading.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中的上下文管理器。医生们是这么说的:
池对象现在支持上下文管理器协议。
__enter__()返回池对象,__exit__()调用terminate()。
terminate()说它这样做:
在未完成未完成的工作的情况下立即停止工作人员处理。当池对象被垃圾收集时,会立即调用terminate()。
很明显,这是一次性使用。
patch上下文管理器暂时对某个对象进行修补,然后在完成修补程序时撤消修补程序。嵌套绝对没有意义-你为什么要重新修补已经修补好的东西?但是,修补、取消修补,然后再进行修补在逻辑上是有意义的,因此它应该是可重用的(测试就是这样)。
我不认为有任何定义明确的东西,你可以说“寻找这个,你就会知道上下文管理器是可重用的/可重用的/一次性的”,因为上下文管理器可以做任何事情。您能做的最好的事情就是了解在__enter__中建立什么上下文,如何分解__exit__,然后逻辑地确定重用/重新输入上下文的含义。
https://stackoverflow.com/questions/25045379
复制相似问题