我可以为条件执行使用Python的with语句吗?

13

我正在尝试编写支持以下语义的代码:

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

但是在action'bypass'时,出现了RuntimeError: generator didn't yield异常。我正在寻找一种支持此功能的方法,而不必回到更冗长的可选实现:

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

有谁知道我如何实现这个目标吗?
谢谢!

P.S. 我正在使用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
“with”不是“if”……您可以编写s.do_something(),并使上下文管理器返回一个什么都不做的对象。 - Jochen Ritzel
考虑过这个问题,但我仍然希望找到一个优雅的解决方案... - Oren S
此外,我事先不了解此范围内代码(例如do_something())的任何内容。 - Oren S
4个回答

7

你试图修改一种基本语言结构的预期行为。这永远都不是一个好主意,它只会导致混淆。

你的解决方法没有问题,但是你可以稍微简化一下。

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

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

您的scope可以改成一个类,其__enter__方法返回一个有用的对象或None,并且它将以相同的方式使用。


1
(这里不想就Python上下文语义展开辩论,)我不同意“基本语言结构的预期行为”这一部分。对我来说,很清楚上下文管理器中的“yield”部分可能会被跳过。直到我捕获了“未yield”异常。 我认为语言应该处理它,可以通过从__enter__抛出预定义的异常类型来实现,类似于next()方法的StopIteration异常。 - Oren S

4
以下方法似乎起作用:
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

我不认为这可以做到。我尝试将上下文管理器实现为一个类,但没有办法强制代码块引发异常,并且随后被__exit__()方法抑制。


我有那种感觉。我希望可以使用一些我通常在这些情况下找到的Python魔法来解决它。虽然看起来是一个不错的功能。也许我可以请求将其作为增强功能... :) - Oren S

-2

我和你有相同的使用场景,并在你提出问题后的这段时间里,发现了一个很有帮助的条件库

从网站上看,它的使用方法如下:

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

2
这不是一个解决方案。conditional 库将条件应用于给定的 上下文管理器 是否已进入。无论如何,都会执行主体。 - bukzor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接