with 语句中的条件或可选上下文管理器
Conditional or optional context managers in with statement
假设我正在使用某种上下文管理器(来自第三方库):
with freeze_time(test_dt):
lines_of_code_1
lines_of_code_2
lines_of_code_3
但是,假设如果 test_dt 没有值,上下文管理器不应该 运行,但所有剩余的代码应该 运行,像这样:
if test_dt:
with freeze_time(test_dt):
lines_of_code_1
lines_of_code_2
lines_of_code_3
else:
lines_of_code_1
lines_of_code_2
lines_of_code_3
假设lines_of_code
这里有2-3行完全相同的代码,有没有更简洁的写法?我知道我可以这样写:
def do_thing():
lines_of_code_1
lines_of_code_2
lines_of_code_3
if test_dt:
with freeze_time(test_dt):
do_thing()
else:
do_thing()
但我并不喜欢这种格式。另外,我不想在我的代码中到处乱扔这种模式。
还有最后一种可能性,但我不确定它是否会起作用:子class上下文管理器并跳过__enter__
和__exit__
函数29=] 给定的是空的,像这样:
class optional_freeze_time(object):
def __init__(self, test_dt=None):
if test_dt:
self.ctx_manager = freeze_time(test_dt)
else:
self.ctx_manager = None
def __enter__(self, *args, **kwargs):
if self.ctx_manager:
self.ctx_manager.__enter__(*args, **kwargs)
def __exit__(self, *args, **kwargs):
if self.ctx_manager:
self.ctx_manager.__exit__(*args, **kwargs)
我用空白上下文管理器 class 对其进行了测试,它似乎运行正常。但是,我担心如果我这样做,真正的上下文管理器是否会正确运行(我不太熟悉它的工作原理)。
这里有一个简单的方法来环绕现有的上下文管理器,甚至不使用任何 类:
from contextlib import contextmanager
@contextmanager
def example_context_manager():
print('before')
yield
print('after')
@contextmanager
def optional(condition, context_manager):
if condition:
with context_manager:
yield
else:
yield
with example_context_manager():
print(1)
with optional(True, example_context_manager()):
print(2)
with optional(False, example_context_manager()):
print(3)
输出:
before
1
after
before
2
after
3
我可能会继承父上下文管理器并编写如下内容:
class BaseContextManager:
def __enter__(self):
print('using Base')
def __exit__(self, *args, **kwargs):
print('exiting Base')
class MineContextManager(BaseContextManager):
def __init__(self, input=None):
self.input = input
def __enter__(self):
if self.input:
super().__enter__()
def __exit__(self, *args, **kwargs):
if self.input:
super().__exit__()
if __name__ == '__main__':
with BaseContextManager():
print('code with base')
with MineContextManager():
print('code without base')
with MineContextManager(input=True):
print('code again with base')
这给出:
using Base
code with base
exiting Base
code without base
using Base
code again with base
exiting Base
只需使用
(freeze_time if test_dt else (lambda func: contextmanager(func))(lambda dt: (yield)))(test_dt)
示例:
from contextlib import contextmanager
test_dt = None
@contextmanager
def freeze_time(test_dt):
print("frozen")
yield
print("unfrozen")
with (freeze_time if test_dt else (lambda func: contextmanager(func))(lambda dt: (yield)))(test_dt):
print("The cold impairs your judgment.")
新访客可能对 contextlib.ExitStack:
感兴趣
with ExitStack() as stack:
if condition:
stack.enter_context(freeze_time(...))
lines_of_code_1
lines_of_code_2
lines_of_code_3
在这个 with
语句之后,freeze_time
仅在条件为真时相关。
假设我正在使用某种上下文管理器(来自第三方库):
with freeze_time(test_dt):
lines_of_code_1
lines_of_code_2
lines_of_code_3
但是,假设如果 test_dt 没有值,上下文管理器不应该 运行,但所有剩余的代码应该 运行,像这样:
if test_dt:
with freeze_time(test_dt):
lines_of_code_1
lines_of_code_2
lines_of_code_3
else:
lines_of_code_1
lines_of_code_2
lines_of_code_3
假设lines_of_code
这里有2-3行完全相同的代码,有没有更简洁的写法?我知道我可以这样写:
def do_thing():
lines_of_code_1
lines_of_code_2
lines_of_code_3
if test_dt:
with freeze_time(test_dt):
do_thing()
else:
do_thing()
但我并不喜欢这种格式。另外,我不想在我的代码中到处乱扔这种模式。
还有最后一种可能性,但我不确定它是否会起作用:子class上下文管理器并跳过__enter__
和__exit__
函数29=] 给定的是空的,像这样:
class optional_freeze_time(object):
def __init__(self, test_dt=None):
if test_dt:
self.ctx_manager = freeze_time(test_dt)
else:
self.ctx_manager = None
def __enter__(self, *args, **kwargs):
if self.ctx_manager:
self.ctx_manager.__enter__(*args, **kwargs)
def __exit__(self, *args, **kwargs):
if self.ctx_manager:
self.ctx_manager.__exit__(*args, **kwargs)
我用空白上下文管理器 class 对其进行了测试,它似乎运行正常。但是,我担心如果我这样做,真正的上下文管理器是否会正确运行(我不太熟悉它的工作原理)。
这里有一个简单的方法来环绕现有的上下文管理器,甚至不使用任何 类:
from contextlib import contextmanager
@contextmanager
def example_context_manager():
print('before')
yield
print('after')
@contextmanager
def optional(condition, context_manager):
if condition:
with context_manager:
yield
else:
yield
with example_context_manager():
print(1)
with optional(True, example_context_manager()):
print(2)
with optional(False, example_context_manager()):
print(3)
输出:
before
1
after
before
2
after
3
我可能会继承父上下文管理器并编写如下内容:
class BaseContextManager:
def __enter__(self):
print('using Base')
def __exit__(self, *args, **kwargs):
print('exiting Base')
class MineContextManager(BaseContextManager):
def __init__(self, input=None):
self.input = input
def __enter__(self):
if self.input:
super().__enter__()
def __exit__(self, *args, **kwargs):
if self.input:
super().__exit__()
if __name__ == '__main__':
with BaseContextManager():
print('code with base')
with MineContextManager():
print('code without base')
with MineContextManager(input=True):
print('code again with base')
这给出:
using Base
code with base
exiting Base
code without base
using Base
code again with base
exiting Base
只需使用
(freeze_time if test_dt else (lambda func: contextmanager(func))(lambda dt: (yield)))(test_dt)
示例:
from contextlib import contextmanager
test_dt = None
@contextmanager
def freeze_time(test_dt):
print("frozen")
yield
print("unfrozen")
with (freeze_time if test_dt else (lambda func: contextmanager(func))(lambda dt: (yield)))(test_dt):
print("The cold impairs your judgment.")
新访客可能对 contextlib.ExitStack:
感兴趣with ExitStack() as stack:
if condition:
stack.enter_context(freeze_time(...))
lines_of_code_1
lines_of_code_2
lines_of_code_3
在这个 with
语句之后,freeze_time
仅在条件为真时相关。