【问题标题】:Break or exit out of "with" statement?中断或退出“with”语句?
【发布时间】:2012-06-27 01:13:42
【问题描述】:

我只想在特定条件下退出with 语句:

with open(path) as f:
    print 'before condition'
    if <condition>: break #syntax error!
    print 'after condition'

当然,以上行不通。有没有办法做到这一点? (我知道我可以反转条件:if not &lt;condition&gt;: print 'after condition' - 任何方式都像上面?)

【问题讨论】:

  • 我很好奇为什么 if not &lt;condition&gt; 在你的情况下是不可取的。
  • @senderle 我认为必须缩进块的其余部分会降低可读性并模糊流程(尤其是在这种情况下,突破感觉像是例外)。想象一下另外三个条件,每个条件后面都有代码块......

标签: python with-statement


【解决方案1】:

这个问题是在 Python 3.4 存在之前提出的,但是在 3.4 中你可以使用contextlib.supress, 压制你自己的个人例外。

查看此(可按原样运行)代码

from contextlib import suppress

class InterruptWithBlock(UserWarning):
    """To be used to interrupt the march of a with"""

condition = True
with suppress(InterruptWithBlock):
    print('before condition')
    if condition: raise InterruptWithBlock()
    print('after condition')

# Will not print 'after condition` if condition is True.

因此,使用问题中的代码,您可以:

with suppress(InterruptWithBlock) as _, open(path) as f:
    print('before condition')
    if <condition>: raise InterruptWithBlock()
    print('after condition')

注意:如果您(仍然)在 3.4 之前,您仍然可以轻松创建自己的 suppress 上下文管理器。

【讨论】:

    【解决方案2】:

    使用while True:

    while True:
        with open(path) as f:
            print 'before condition'
            if <condition>: 
                break 
            print 'after condition n'
        break
    

    【讨论】:

      【解决方案3】:

      这是使用tryexcept 的另一种方法,尽管反转条件可能更方便。

      class BreakOut(Exception): pass
      
      try:
          with open(path) as f:
              print('before condition')
              if <condition>: 
                  raise BreakOut #syntax error!
              print('after condition')
      except BreakOut:
          pass
      

      【讨论】:

        【解决方案4】:

        从“break”改为“f.close”

        with open(path) as f:
            print('before condition')
            if <condition>: f.close()
            print('after condition')
        

        【讨论】:

        • 一方面,它没有一概而论。问题一般是关于 with 语句,文件上下文管理器只是一个示例。
        【解决方案5】:

        为此目的存在一个函数__exit__()。语法如下:

        with VAR = EXPR:
          try:
            BLOCK
          finally:
            VAR.__exit__()
        

        【讨论】:

          【解决方案6】:

          这是一个古老的问题,但这是方便的“可破坏范围”成语的应用。只需将您的 with 声明嵌入其中:

          for _ in (True,):
              with open(path) as f:
                  print 'before condition'
                  if <condition>: break
                  print 'after condition'
          

          这个习惯用法创建了一个“循环”,总是只执行一次,其唯一目的是将代码块包含在一个可以有条件地打破的范围内。在 OP 的情况下,它是一个被封闭的上下文管理器调用,但它可以是任何可能需要条件转义的有界语句序列。

          公认的答案很好,但是这种技术不需要创建函数就可以做同样的事情,这并不总是方便或不希望的。

          【讨论】:

          • 我喜欢这显然只执行一次(而不是 while True 变体),并且您包含了成语的名称。
          【解决方案7】:

          with给你添麻烦了?在问题上抛出更多with-able 对象!

          class fragile(object):
              class Break(Exception):
                """Break out of the with statement"""
          
              def __init__(self, value):
                  self.value = value
          
              def __enter__(self):
                  return self.value.__enter__()
          
              def __exit__(self, etype, value, traceback):
                  error = self.value.__exit__(etype, value, traceback)
                  if etype == self.Break:
                      return True
                  return error
          

          只需用fragileraise fragile.Break 将您要发送到with 的表达式包装起来,即可随时爆发!

          with fragile(open(path)) as f:
              print 'before condition'
              if condition:
                  raise fragile.Break
              print 'after condition'
          

          这种设置的好处

          • 使用withwith;不会将您的函数包装在语义上具有误导性的单次运行“循环”或狭义的专用函数中,也不会强迫您在 with 之后进行任何额外的错误处理。
          • 保持局部变量可用,而不必将它们传递给包装函数。
          • 可嵌套!

            with fragile(open(path1)) as f:
                with fragile(open(path2)) as g:
                    print f.read()
                    print g.read()
                    raise fragile.Break
                    print "This wont happen"
                print "This will though!"
            

            这样,如果您想同时中断,则不必创建新函数来包装外部 with

          • 根本不需要重组:只需用fragile 包装已有的内容,一切顺利!

          这种设置的缺点

          • 实际上并未使用“break”语句。不能全部赢;)

          【讨论】:

          • 我认为这是比大多数其他设置更好的设置。不需要重构代码,fragile() 是可重用的。
          • 刚回来,我很喜欢这个。
          • 我认为更大的缺点是调用已包装的上下文管理器的方法有点笨拙。我喜欢通用解决方案,但会将其实现为静态函数,通过包装当前存在的 enterenterexit b>退出。可能还有一些奇特的方法可以用装饰器来做到这一点......
          • 它很聪明,使用了装饰器模式(我最喜欢的),但它对控制流使用异常处理。我在围栏上。
          • 这是一个聪明的想法,我正在调整它以允许我仍然使用上下文管理器,同时如果在处理过程中发生异常,有条件地跳过清理 with'd 资源(临时文件),允许调试。
          【解决方案8】:

          简写为 sn-p:

          class a:
              def __enter__(self):
                  print 'enter'
              def __exit__(self ,type, value, traceback):
                  print 'exit'
          
          for i in [1]:
              with a():
                  print("before")
                  break
                  print("after")
          

          ...

          enter
          before
          exit
          

          【讨论】:

          • 这让人想起 Python C API 有时如何将宏的内容放在一个 dowhile 循环中,只不过是在末尾获取分号。
          • 实际使用元组(如上面的stackoverflow.com/a/43795918)还是列表(如这里)更有效?
          【解决方案9】:

          由于break 只能出现在循环内,您的选项在with 内受到一定限制:

          • return(将“with”+相关语句放入函数中)
          • 退出(从计划中保释 - 可能不理想)
          • 异常(在“with”内生成异常,在下方捕获)

          拥有一个函数并使用return 可能是这里最干净和最简单的解决方案,如果您可以在函数内隔离with 和相关语句(仅此而已)。

          否则在需要时在with 内生成异常,在with 下方/外部立即捕获以继续其余代码。

          更新:正如 OP 在下面的 cmets 中建议的那样(也许是脸颊上的舌头?),也可以将 with 语句包装在一个循环中以使 break 工作 - 尽管那会在语义上具有误导性。因此,虽然是一个可行的解决方案,但可能不是推荐的东西)。

          【讨论】:

          • 是的,我知道;我想知道是否有什么确实有效。
          • @Matthias 是的,我想到了 - 似乎将它包装在一个函数中更简洁。
          • @Levon 我实际上尝试使用return 进行检查,但您只能从功能块返回,而不能从 with 块返回。我相信你知道这一点,但你写它的方式听起来像是返回只会停止执行 with 中的块的其余部分,而实际上它也阻止了 with 块之后的任何代码,但在相同的功能。
          • @Matthias 事实上,我经常发现自己希望with 块就像try 块一样,并且可以干净地跟随异常情况。如果可以的话,我会这样做。
          • @Levon 我想我们可以在您的列表中添加一个选项:将 with 包裹在一个愚蠢的 for 循环中,例如 for i in [0],以便您可以中断。
          【解决方案10】:

          我认为你应该重构逻辑:

          with open(path) as f:
              print 'before condition checked'
              if not <condition>:
                  print 'after condition checked'
          

          【讨论】:

          • @jmilloy,我认为这至少与将with 语句封装在函数中并使用return 或引发异常一样好。坦率地说,在许多情况下,我认为这将是最优雅和可读的解决方案。
          • @senderle 同意 100%。然而,这根本不是问题。
          • 好吧,我想如果我们讨论的是 Python 不允许的假设性较差的构造,我们可能会抱怨缺少一个像样的 goto 语句 :-) entrian.com/goto
          【解决方案11】:
          f = open("somefile","r")
          for line in f.readlines():
                 if somecondition: break;
          f.close()
          

          我不认为你可以打破...你需要使用循环...

          [编辑] 或者只是做其他人提到的功能方法

          【讨论】:

            【解决方案12】:

            你可以把所有东西都放在一个函数中,当条件为真时调用一个 return。

            【讨论】:

              【解决方案13】:

              最好的方法是将其封装在一个函数中并使用return

              def do_it():
                  with open(path) as f:
                      print 'before condition'
                      if <condition>:
                          return
                      print 'after condition'
              

              【讨论】:

              • 这会返回,但不会完全“突破” with 语句。用户必须注意__exit__ 无论如何都会被执行。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-06
              • 2016-07-19
              相关资源
              最近更新 更多