Подтвердить что ты не робот

Можно ли использовать python с инструкцией для условного выполнения?

Я пытаюсь написать код, который поддерживает следующую семантику:

with scope('action_name') as s:
  do_something()
  ...
do_some_other_stuff()

Объем, среди прочего (настройка, очистка), должен решить, должен ли этот раздел работать.
Например, если пользователь настроил программу на обход "action_name", чем после оценки Scope() do_some_other_stuff() будет выполняться без вызова do_something().
Я попытался сделать это, используя этот менеджер контекста:

@contextmanager
def scope(action):
  if action != 'bypass':
    yield

но получил исключение RuntimeError: generator didn't yield (когда action - 'bypass').
Я ищу способ поддержать это, не отступая от более подробной опционной реализации:

with scope('action_name') as s:
  if s.should_run():
    do_something()
    ...
do_some_other_stuff()

Кто-нибудь знает, как я могу это достичь?
Спасибо!

P.S. Я использую python2.7

EDIT:
Решение не обязательно должно полагаться на выражения with. Я просто не знал точно, как выразить это без него. В сущности, я хочу что-то в форме контекста (поддерживая установку и автоматическую очистку, не связанную с логикой) и позволяя условное выполнение на основе параметров, переданных методу настройки и выбранных в конфигурации.
Я также подумал о возможном решении с использованием декораторов. Пример:

@scope('action_name') # if 'action_name' in allowed actions, do:
                      #   setup()
                      #   do_action_name()
                      #   cleanup()
                      # otherwise return
def do_action_name()
  do_something()

но я не хочу принуждать слишком много внутренней структуры (то есть, как код делится на функции) на основе этих областей.
У кого-нибудь есть творческие идеи?

4b9b3361

Ответ 1

Вы пытаетесь изменить ожидаемое поведение базовой языковой конструкции. Это никогда не будет хорошей идеей, это просто приведет к путанице.

Нет ничего плохого в вашей работе, но вы можете немного упростить его.

@contextmanager 
def scope(action): 
  yield action != 'bypass'

with scope('action_name') as s: 
  if s: 
    do_something() 
    ... 
do_some_other_stuff() 

Вместо этого scope может быть классом, метод __enter__ возвращает либо полезный объект, либо None, и он будет использоваться таким же образом.

Ответ 2

Я не думаю, что это можно сделать. Я попробовал реализовать диспетчер контекста как класс, и просто не было способа заставить блок создать исключение, которое впоследствии будет подавлено с помощью метода __exit__().

Ответ 3

Кажется, что работает следующее:

from contextlib import contextmanager

@contextmanager
def skippable():
    try:
        yield
    except RuntimeError as e:
        if e.message != "generator didn't yield":
            raise

@contextmanager
def context_if_condition():
    if False:
        yield True

with skippable(), context_if_condition() as ctx:
    print "won't run"

Вопросы:

  • нужно, чтобы кто-то придумал лучшие имена
  • context_if_condition не может использоваться без skippable, но нет способа принудительно выполнить это/удалить избыточность
  • он может улавливать и подавлять RuntimeError из более глубокой функции, чем предполагалось (это может помочь пользовательское исключение, но это делает всю конструкцию беспорядочной)
  • это не яснее, чем просто использовать версию @Mark Ransom

Ответ 4

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

С сайта его использование:

with conditional(CONDITION, CONTEXTMANAGER()):
    BODY()