55

我只想with在某些条件下退出语句:

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

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

4

13 回答 13

83

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

只需将您要使用的表达式包装with起来fragile,并raise fragile.Break随时爆发!

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

此设置的好处

  • 使用with和只是with; 不会将您的函数包装在语义上具有误导性的单次运行“循环”或狭义的专用函数中,并且不会强迫您在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'语句。不能赢得所有人;)
于 2014-05-14T21:43:07.840 回答
48

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

def do_it():
    with open(path) as f:
        print 'before condition'
        if <condition>:
            return
        print 'after condition'
于 2012-06-25T18:33:55.350 回答
12

This is an ancient question, but this is an application for the handy "breakable scope" idiom. Just imbed your with statement inside:

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

This idiom creates a "loop", always executed exactly once, for the sole purpose of enclosing a block of code inside a scope that can be broken out of conditionally. In OP's case, it was a context manager invocation to be enclosed, but it could be any bounded sequence of statements that may require conditional escape.

The accepted answer is fine, but this technique does the same thing without needing to create a function, which is not always convenient or desired.

于 2017-05-05T03:13:51.310 回答
9

我认为你应该重构逻辑:

with open(path) as f:
    print 'before condition checked'
    if not <condition>:
        print 'after condition checked'
于 2012-06-25T18:47:18.480 回答
7

由于break只能在循环内发生,因此您的选项在以下范围内受到限制with

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

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

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

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

于 2012-06-25T18:31:44.143 回答
2
f = open("somefile","r")
for line in f.readlines():
       if somecondition: break;
f.close()

我认为您无法摆脱...您需要使用循环...

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

于 2012-06-25T18:34:53.663 回答
2

作为速记片段:

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
于 2014-05-13T08:16:23.520 回答
1

使用while True

while True:
    with open(path) as f:
        print 'before condition'
        if <condition>: 
            break 
        print 'after condition n'
    break
于 2020-10-08T22:17:00.207 回答
1

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

with VAR = EXPR:
  try:
    BLOCK
  finally:
    VAR.__exit__()
于 2018-03-13T20:09:13.773 回答
0

您可以将所有内容放在一个函数中,当条件为真时调用返回。

于 2012-06-25T18:34:30.187 回答
0

这是使用tryand的另一种方法except,即使反转条件可能更方便。

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
于 2020-11-14T12:36:22.387 回答
0

这个问题是在 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轻松制作自己的上下文管理器。

于 2021-11-05T21:20:27.973 回答
-2

从“break”更改为“f.close”

with open(path) as f:
    print('before condition')
    if <condition>: f.close()
    print('after condition')
于 2019-12-12T04:26:57.387 回答