如何在 Python 中编写空(无操作)上下文管理器?
How do I write a null (no-op) contextmanager in Python?
有时我需要一个什么都不做的虚拟上下文管理器。然后它可以用作更有用但可选的上下文管理器的替代品。例如:
ctx_mgr = <meaningfulContextManager> if <condition> else <nullContextManager>
with ctx_mgr:
...
如何定义如此琐碎、空洞的上下文管理器? Python 图书馆是否提供现成的?
我们希望将上下文与 as
子句一起使用的情况如何?
with ctx_mgr as resource:
<operations on resource>
Python3.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 之前,或者 如果你需要在 [=56= 之前的 as
子句 ] 3.7、开发者需要自己滚。
这是一种可能的实现方式(见底部的注释,但所有错误都是我的):
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
需要支持 "meaningful" 资源所需的所有操作。因此,例如,如果有意义的上下文管理器,在 __enter__()
、returns 上对托管块内的 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. Many thanks to all those who contributed to that discussion. This is my attempt at summarising its outcome in a self-answered question, to save people time reading that long thread. Also see Python documentation's mention of this use of ExitStack
.
我只是将 threading.Lock()
用作虚拟上下文管理器。临时锁,仅供上下文管理器使用。
自 Python 3.2 起,memoryview(b'')
可用作空操作上下文管理器。参见 https://docs.python.org/3/library/stdtypes.html#memoryview.release。
优点
无需导入
适用于 3.2+
大约是 contextlib.nullcontext
的两倍
缺点
- 您可能想添加
# no-op
评论。
Python 3.6 及以下版本(包括 2.7)的简单解决方案:
from contextlib import contextmanager
@contextmanager
def nullcontext(enter_result=None):
yield enter_result
从 Python 3.7 开始,您应该改用提供的 contextlib.nullcontext
。
对于 Python 2.7+ ,您可以使用
import contextlib
with (lambda noop_func: contextlib.contextmanager(noop_func))(lambda: (yield))():
print("hi")
Python 2.7 的简单解决方案,答案中尚未提及:
from contextlib import nested
with nested():
...
有时我需要一个什么都不做的虚拟上下文管理器。然后它可以用作更有用但可选的上下文管理器的替代品。例如:
ctx_mgr = <meaningfulContextManager> if <condition> else <nullContextManager>
with ctx_mgr:
...
如何定义如此琐碎、空洞的上下文管理器? Python 图书馆是否提供现成的?
我们希望将上下文与 as
子句一起使用的情况如何?
with ctx_mgr as resource:
<operations on resource>
Python3.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 之前,或者 如果你需要在 [=56= 之前的 as
子句 ] 3.7、开发者需要自己滚。
这是一种可能的实现方式(见底部的注释,但所有错误都是我的):
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
需要支持 "meaningful" 资源所需的所有操作。因此,例如,如果有意义的上下文管理器,在 __enter__()
、returns 上对托管块内的 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. Many thanks to all those who contributed to that discussion. This is my attempt at summarising its outcome in a self-answered question, to save people time reading that long thread. Also see Python documentation's mention of this use of ExitStack
.
我只是将 threading.Lock()
用作虚拟上下文管理器。临时锁,仅供上下文管理器使用。
自 Python 3.2 起,memoryview(b'')
可用作空操作上下文管理器。参见 https://docs.python.org/3/library/stdtypes.html#memoryview.release。
优点
无需导入
适用于 3.2+
大约是
contextlib.nullcontext
的两倍
缺点
- 您可能想添加
# no-op
评论。
Python 3.6 及以下版本(包括 2.7)的简单解决方案:
from contextlib import contextmanager
@contextmanager
def nullcontext(enter_result=None):
yield enter_result
从 Python 3.7 开始,您应该改用提供的 contextlib.nullcontext
。
对于 Python 2.7+ ,您可以使用
import contextlib
with (lambda noop_func: contextlib.contextmanager(noop_func))(lambda: (yield))():
print("hi")
Python 2.7 的简单解决方案,答案中尚未提及:
from contextlib import nested
with nested():
...