有时我需要一个无效的虚拟上下文管理器。然后,它可以用作更有用但可选的上下文管理器的替身。例如:
ctx_mgr = <meaningfulContextManager> if <condition> else <nullContextManager>
with ctx_mgr:
...
如何定义这样一个简单的空上下文管理器? Python库是否提供现成的产品?
我们希望将上下文与as
子句一起使用的情况如何?
with ctx_mgr as resource:
<operations on resource>
Python 3.7及以上版本:使用专门为此设计的contextlib.nullcontext
。
在Python 3.7之前,标准库不提供专门为这些用例设计的上下文管理器,但有一些解决方法。
从Python 3.4开始,contextlib.suppress
可以在第一种情况下用于此目的,即当没有as
子句时:
ctx_mgr = <meaningfulContextManager> if <condition> else contextlib.suppress()
with ctx_mgr:
...
从Python 3.3开始,类似的解决方法也可用,contextlib.ExitStack
,虽然比suppress
慢(在我的测试中需要两倍的时间)。
在Python 3.3之前,或者如果你在Python 3.7之前需要一个as
子句,开发人员需要自己动手。这是一个可能的实现(请参阅底部的注释,但所有错误都是我的):
class NullContextManager(object):
def __init__(self, dummy_resource=None):
self.dummy_resource = dummy_resource
def __enter__(self):
return self.dummy_resource
def __exit__(self, *args):
pass
然后可以写:
ctx_mgr = <meaningfulContextManager> if <condition> else NullContextManager(dummy_resource)
with ctx_mgr as resource:
<operations on resource>
当然,dummy_resource
需要支持“有意义”资源所需的所有操作。因此,例如,如果__enter__()
上有意义的上下文管理器返回在托管块内部对quack()
进行的操作,那么dummy_resource
也需要支持它,尽管可能根本没有做任何事情。
class DummyDuck(object):
def quack()
# Ssssh...
pass
ctx_mgr = <meaningfulContextManager> if <condition> else NullContextManager(DummyDuck())
with ctx_mgr as someDuck:
someDuck.quack()
资料来源:A Python feature request。非常感谢为讨论做出贡献的所有人。这是我尝试在一个自我回答的问题中总结其结果,以节省人们阅读这个长线程的时间。另请参阅Python文档中提到的this use of ExitStack
。
Python 3.6及更低版本的简单解决方案,包括2.7:
from contextlib import contextmanager
@contextmanager
def nullcontext(enter_result=None):
yield enter_result
从Python 3.7开始,您应该使用提供的contextlib.nullcontext
代替。
从Python 3.2开始,memoryview(b'')
可以用作无操作上下文管理器。见https://docs.python.org/3/library/stdtypes.html#memoryview.release。
优点
contextlib.nullcontext
的两倍缺点
# no-op
评论。我只是使用threading.Lock()
作为虚拟上下文管理器。临时锁定,仅由上下文管理器使用。