如何在Python中编写null(无操作)上下文管理器?

时间:2017-07-19 10:04:05

标签: python contextmanager

有时我需要一个无效的虚拟上下文管理器。然后,它可以用作更有用但可选的上下文管理器的替身。例如:

ctx_mgr = <meaningfulContextManager> if <condition> else <nullContextManager>
with ctx_mgr:
    ...

如何定义这样一个简单的空上下文管理器? Python库是否提供现成的产品?

我们希望将上下文与as子句一起使用的情况如何?

with ctx_mgr as resource:
    <operations on resource>

6 个答案:

答案 0 :(得分:31)

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需要支持&#34;有意义的&#34;所需的所有操作。资源。因此,例如,如果有意义的上下文管理器在__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

答案 1 :(得分:0)

从Python 3.2开始,memoryview(b'')可以用作无操作上下文管理器。参见https://docs.python.org/3/library/stdtypes.html#memoryview.release

专业人士

  • 无需导入

  • 适用于3.2 +

  • 大约是contextlib.nullcontext

  • 的两倍

缺点

  • 您可能想添加# no-op评论。

答案 2 :(得分:0)

适用于Python 3.6及更低版本(包括2.7)的简单解决方案:

from contextlib import contextmanager

@contextmanager
def nullcontext(enter_result=None):
    yield enter_result

从Python 3.7开始,您应该改用提供的contextlib.nullcontext

答案 3 :(得分:0)

Python 2.7 的简单解决方案,答案中尚未提及:

from contextlib import nested
with nested():
    ...

答案 4 :(得分:-1)

对于Python 2.7+,您可以使用

import contextlib
with (lambda noop_func: contextlib.contextmanager(noop_func))(lambda: (yield))():
    print("hi")

答案 5 :(得分:-3)

我刚用threading.Lock()作为虚拟上下文管理器。临时锁定,仅由上下文管理器使用。