with语句中的条件或可选上下文管理器

时间:2016-12-20 21:53:22

标签: python contextmanager

假设我有一些我正在使用的上下文管理器(来自第三方库):

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)

我使用空白的上下文管理器类测试了它,它似乎表现正常。但是,如果我这样做,我担心真正的上下文管理器是否会正常运行(我对它的工作方式不太熟悉)。

4 个答案:

答案 0 :(得分:5)

这是一种简单的方法来包装现有的上下文管理器,甚至不使用任何类:

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

只需使用

library(data.table)
DT <- fread("Event   DT.event
off     4/27/12_17:25:13     
on      4/27/12_17:25:39     
off     4/27/12_18:03:29     
on      4/27/12_18:03:57     
off     4/27/12_19:41:16     
on      4/27/12_19:43:50     
off     4/28/12_6:23:57      
on      4/28/12_6:32:13      
off     4/28/12_6:40:20      
on      4/28/12_6:40:48      
off     4/28/12_8:16:07")      

示例:

(freeze_time if test_dt else (lambda func: contextmanager(func))(lambda dt: (yield)))(test_dt)