【问题标题】:Conditional or optional context managers in with statementwith 语句中的条件或可选上下文管理器
【发布时间】:2017-05-06 05:34:03
【问题描述】:

假设我正在使用某种上下文管理器(来自第三方库):

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()

但我并不喜欢这种格式。另外,我不想在我的代码中到处乱扔这种模式。

还有最后一种可能性,但我不确定它是否会起作用:如果给定的 test_dt 为空,则子类化上下文管理器并跳过 __enter____exit__ 函数,如下所示:

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)

我用一个空白的上下文管理器类对其进行了测试,它似乎表现正确。但是,我担心如果我这样做,真正的上下文管理器是否会正确运行(我不太熟悉它的内部工作原理)。

【问题讨论】:

  • 你能举一个不那么抽象的例子吗?是的,您可以更改上下文管理器,使其在进入和退出if input is None: 时不执行任何操作。你测试过你写的东西吗?
  • 也许可以创建一个不执行任何操作的 blank_context_manager 对象并覆盖 context_manager.__new__ 以在没有输入的情况下返回空白,这会将条件数减少到 1。
  • @jonrsharpe 您希望代码的哪一部分不那么抽象?我宁愿不改变或触摸原来的上下文管理器。我将继续用我正在使用的实际上下文管理器替换代码,但我觉得这个概念本身可以适用于更一般的情况。

标签: python contextmanager


【解决方案1】:

这是一种无需使用任何类即可封装现有上下文管理器的简单方法:

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

【讨论】:

  • 我同意其他人的观点,即这是一个重复的问题,但我更喜欢你的答案,而不是已经存在的答案!也许将其发布在另一页上作为答案? stackoverflow.com/questions/27803059/…
【解决方案2】:

我可能会从父上下文管理器继承并编写如下内容:

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

【讨论】:

    【解决方案3】:

    随便用

    (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.")
    

    【讨论】:

      【解决方案4】:

      新访问者可能对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 仅在条件为真时才相关。

      【讨论】:

        猜你喜欢
        • 2012-08-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-15
        • 2017-03-29
        • 1970-01-01
        • 2021-05-09
        相关资源
        最近更新 更多