Могу ли я использовать python с оператором для условного выполнения? - PullRequest
11 голосов
/ 30 ноября 2010

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

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

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

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

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

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

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

Ответы [ 4 ]

7 голосов
/ 01 декабря 2010

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

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

@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 голосов
/ 29 октября 2016

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

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
1 голос
/ 30 ноября 2010

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

0 голосов
/ 11 декабря 2014

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

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

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