2016-12-20 3 views
3

Предположим, у меня есть какой-то менеджер контекста (из библиотеки третьей стороной), что я использую, как так:Условные или дополнительные управляющие контекстные с утверждением

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

Но я не сумасшедший об этом форматировании. Кроме того, я не хочу, чтобы мусор этот шаблон по всему моему коду.

Существует одна последняя возможности, но я не уверен, что это будет работать: подклассы менеджера контекста и пропуская __enter__ и __exit__ функции, если test_dt данный пуст, так как:

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) 

Я тестировал это с пустым классом менеджера контекста, и, похоже, он ведет себя правильно. Тем не менее, я беспокоюсь, будет ли реальный менеджер контекста вести себя правильно, если я это сделаю (я не очень хорошо знаком с внутренними принципами того, как он работает).

+1

Не могли бы вы дать менее абстрактный пример? Да, вы могли бы изменить диспетчер контекста, чтобы он не делал все, что он делает при вводе и выходе из 'if input is None:'. Вы проверили, что вы написали? – jonrsharpe

+0

может сделать объект 'blank_context_manager', который ничего не делает и переопределяет' context_manager .__ new__', чтобы вернуть пустой, если нет ввода, уменьшит количество условных выражений до 1. –

+1

Возможный дубликат [Условный оператор с Python] (http://stackoverflow.com/questions/27803059/conditional-with-statement-in-python) –

ответ

4

Вот простой способ, чтобы обернуть вокруг существующего менеджера контекста даже без использования каких-либо классов:

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 
+0

Я согласен с другими, что это дублированный вопрос, но мне нравится ваш ответ намного лучше, чем те, которые уже есть! Может быть, опубликовать его на другой странице в качестве ответа? http://stackoverflow.com/questions/27803059/conditional-with-statement-in-python –

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