Можно ли использовать 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()
но я не хочу принуждать слишком много внутренней структуры (то есть, как код делится на функции) на основе этих областей.
У кого-нибудь есть творческие идеи?
Ответы
Ответ 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()