如何在Python中打破多个循环?

Mat*_*ley 440 python break control-flow

给出以下代码(不起作用):

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok.lower() == "y": break 2 #this doesn't work :(
        if ok.lower() == "n": break
    #do more processing with menus and stuff
Run Code Online (Sandbox Code Playgroud)

有没有办法让这项工作?或者我是否已经做了一次检查以突破输入循环,然后另一个更有限的检查外部循环以在用户满意的情况下将所有内容分开?

Edit-FYI: get_input是我编写的一个简短函数,支持显示提示和默认值以及所有那些幻想和返回__CODE__

Rob*_*ney 457

我的第一直觉是将嵌套循环重构为函数并用于return突破.

  • 在这个特定情况下同意,但在"我有嵌套循环,我该怎么做"的一般情况下,重构可能没有意义. (85认同)
  • 我同意努力使用`return`是正确的方法.理由是,根据Python_的_Zen,"flat比嵌套更好".我们在这里有三个级别的嵌套,如果它开始妨碍,是时候减少嵌套或者至少将整个嵌套提取到它自己的函数中. (7认同)
  • 它通常可以将内部循环重构为自己的方法,返回true继续,false返回外部循环.while condition1:/如果不是MyLoop2(params):休息.另一种方法是设置一个布尔标志,在两个级别进行测试.more = True/while condition1和更多:/ while condition2和更多:/ if stopCondition:more = False/break/... (5认同)
  • 这是我的另一个想法,因为get_input_yn()函数在其他地方也很有用,我敢肯定. (3认同)
  • 嵌套循环只是必要的。您的嵌套不应超过 3 个,但嵌套循环是使查找和扫描起作用的原因,因此即使您没有自己编写它们,您也会使用它们。必须有一种方法来打破多个循环,因为这是我所知道的所有其他编程语言的基本必需品。 (3认同)
  • 我知道这似乎很明显,但是使用原始代码的示例可以改进这个答案。 (2认同)

小智 193

这是另一种简短的方法.缺点是你只能打破外循环,但有时它正是你想要的.

for a in xrange(10):
    for b in xrange(20):
        if something(a, b):
            # Break the inner loop...
            break
    else:
        # Continue if the inner loop wasn't broken.
        continue
    # Inner loop was broken, break the outer.
    break
Run Code Online (Sandbox Code Playgroud)

这使用了for/else构造解释:为什么python在for和while循环之后使用'else'?

关键见解:似乎外环总是断裂.但是如果内部循环没有破坏,外部循环也不会.

continue声明是这里的神奇.它在for-else子句中.根据定义,如果没有内部中断,就会发生这种情况.在那种情况下,continue整齐地绕开了外围的突破.

  • @eugeney为什么不呢?第一次休息将退出内循环. (6认同)
  • 这很聪明。:-) 但是,这并不简单。坦率地说,我并不相信在 Python 中保留标记为“break”或“break(n)”的论点。这些解决方法增加了更多的复杂性。 (5认同)
  • @eugeney我觉得我在这里错过了一些东西.你能发一个例子吗? (4认同)
  • @Mingliang表示可以继续前进. (4认同)
  • 这在以下情况下不起作用。如果内部循环中有两个中断,其中一个旨在仅中断内部循环,而另一个旨在中断两个循环 (3认同)
  • 这是从 Raymond Hettinger 视频中得到的,https://youtu.be/OSGv2VnC0go?t=971,将 for 循环附加的“else”语句读为“no_break”,然后就变得更容易理解了。 (2认同)

Joh*_*uhy 144

PEP 3136提出标记的中断/继续.Guido 拒绝了它,因为"代码如此复杂,需要此功能非常罕见".PEP确实提到了一些解决方法(例如异常技术),而Guido认为在大多数情况下使用return的重构会更简单.

  • 虽然,重构/"返回"通常是要走的路,但我已经看到了很多情况,其中一个简单的"破2"语句就是如此有意义.另外,对于`continue`,refactor /`return`不起作用.在这些情况下,数字中断和继续将更容易遵循,而不是重构到一个微小的函数,引发异常,或涉及在每个嵌套级别设置标志中断的复杂逻辑.遗憾的是Guido拒绝了它. (69认同)
  • `突破; 休息`会很好. (8认同)
  • @Jeyekomon问题是您不需要3个或更多嵌套循环即可解决此问题。2个嵌套循环很常见 (4认同)
  • 显然,我只能编辑 5 分钟的帖子(已经 6 分钟了)。所以,这是我编辑过的帖子:我的 2 美分:Perl 标记了 break(但称其为“last”)和“next”以直接进行下一次迭代。它并不罕见 - 我一直在使用它。我是 Python 的新手,并且已经需要它。此外,编号中断对于重构来说是可怕的 - 最好标记您想要脱离的循环,然后使用 break <label> 明确说明您想要脱离的循环。 (3认同)
  • “如此复杂以至于需要此功能的代码很少见”。但是,如果您曾经使用过如此复杂的代码,则缺少标记循环将使其变得更加复杂,因为您必须在所有循环中手动转发“ break”。笨。 (2认同)

S.L*_*ott 108

首先,普通逻辑是有帮助的.

如果由于某种原因,终止条件无法解决,例外是一个后备计划.

class GetOutOfLoop( Exception ):
    pass

try:
    done= False
    while not done:
        isok= False
        while not (done or isok):
            ok = get_input("Is this ok? (y/n)")
            if ok in ("y", "Y") or ok in ("n", "N") : 
                done= True # probably better
                raise GetOutOfLoop
        # other stuff
except GetOutOfLoop:
    pass
Run Code Online (Sandbox Code Playgroud)

对于此特定示例,可能不需要例外.

另一方面,我们在字符模式应用程序中经常使用"Y","N"和"Q"选项.对于"Q"选项,我们希望立即退出.这更加特殊.

  • 有趣的想法.我是否喜欢它或讨厌它. (12认同)
  • 如果分别显示两个变体,这个解决方案会更有帮助.(1)使用标志(`done`).(2)提出异常.将它们合并为一个解决方案只会使它看起来很复杂.对于未来的读者:使用涉及`done`的所有行,或者定义`GetOutOfLoop(Exception)`并引发/除此之外. (8认同)
  • 通常,使用try-blocks来处理其他任何事情,那么异常就是非常不受欢迎的.Try-blocks是专门为错误处理而设计的,并且在一些奇怪的控制流程中使用它们在风格上并不是很好. (4认同)
  • 说真的,异常是*非常便宜,惯用的python使用了很多很多.定义和抛出自定义的内容也非常容易. (3认同)
  • 在 Python3 中,这将是 `raise Exception('GetOutOfLoop')` 和 `except Exception:`。 (2认同)
  • @ tommy.carstensen废话;定义一个新的异常子类并将其引发(如答案所示)并将自定义消息传递给Exception构造函数(例如raise Exception('bla bla bla')`)在Python 2和Python 3中均有效在这种情况下,前者是可取的,因为我们不希望我们的`except`块捕获* all *异常,而只希望我们用来退出循环的特殊异常。如果我们按照您的建议进行操作,然后代码中的错误导致引发意外的异常,则将其与故意退出循环一样被错误地对待。 (2认同)

Mar*_*son 51

我倾向于同意重构函数通常是这种情况的最佳方法,但是当你真的需要打破嵌套循环时,这是@ S.Lott描述的异常提升方法的有趣变体.它使用Python的with语句使异常提升看起来更好一些.使用以下命令定义新的上下文管理器(您只需执行一次):

from contextlib import contextmanager
@contextmanager
def nested_break():
    class NestedBreakException(Exception):
        pass
    try:
        yield NestedBreakException
    except NestedBreakException:
        pass
Run Code Online (Sandbox Code Playgroud)

现在您可以使用此上下文管理器,如下所示:

with nested_break() as mylabel:
    while True:
        print "current state"
        while True:
            ok = raw_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": raise mylabel
            if ok == "n" or ok == "N": break
        print "more processing"
Run Code Online (Sandbox Code Playgroud)

优点:(1)它稍微清洁(没有明确的try-except块),以及(2)你Exception为每次使用获得一个定制的子类nested_break; Exception每次都不需要声明自己的子类.


Mat*_*t J 38

首先,您还可以考虑将输入的获取和验证过程作为一个函数; 在该函数中,如果值正确则可以返回值,如果不正确则继续在while循环中旋转.这基本上消除了您解决的问题,并且通常可以应用于更一般的情况(打破多个循环).如果你绝对必须在你的代码中保留这个结构,并且真的不想处理簿记布尔...

您也可以通过以下方式使用goto(使用此处的愚人节模块):

#import the stuff
from goto import goto, label

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y": goto .breakall
        if ok == "n" or ok == "N": break
    #do more processing with menus and stuff
label .breakall
Run Code Online (Sandbox Code Playgroud)

我知道,我知道,"你不应该使用goto"等等,但它在这样的奇怪情况下效果很好.

  • 我认为这是一个干净,可读的解决方案,可以作为优秀的代码,所以我投票了.:) (12认同)
  • 我喜欢这个笑话,但堆栈溢出点是为了促进良好的代码,所以我必须投票给你:( (3认同)
  • goto的代表不好,我认为任何专业的编码人员都应该能够正确处理它。 (2认同)

小智 29

引入一个新变量,将其用作"循环断路器".首先为它指定一些东西(False,0等),然后,在外部循环中,在你打破它之前,将值更改为其他(True,1,...).循环退出后,"父"循环检查该值.让我来证明:

breaker = False #our mighty loop exiter!
while True:
    while True:
        if conditionMet:
            #insert code here...
            breaker = True 
            break
    if breaker: # the interesting part!
        break   # <--- !
Run Code Online (Sandbox Code Playgroud)

如果你有一个无限循环,这是唯一的出路; 对于其他循环执行真的要快得多.如果您有许多嵌套循环,这也适用.你可以退出所有,或只退出几个.无限可能!希望这有帮助!

  • 问题的关键是要避免这种混乱. (4认同)
  • 尽管这是最容易应用的,但是当您想要退出超过 2 个循环时,它就会变得很麻烦。 (4认同)

Jus*_*tas 17

要打破多个嵌套循环而不重构函数,请使用带有内置StopIteration异常的"模拟goto语句" :

try:
    for outer in range(100):
        for inner in range(100):
            if break_early():
                raise StopIteration

except StopIteration: pass
Run Code Online (Sandbox Code Playgroud)

这个讨论关于使用goto语句的嵌套循环的突破.

  • 这看起来比创建自己的类来处理异常要好得多,而且看起来非常干净。我有什么理由不应该这样做吗? (2认同)
  • 事实上 StopIteration 用于生成器,但我认为通常不会有任何未捕获的 StopIteration 异常。所以这似乎是一个很好的解决方案,但无论如何创建新的异常并没有错误。 (2认同)

qui*_*dry 15


keeplooping=True
while keeplooping:
    #Do Stuff
    while keeplooping:
          #do some other stuff
          if finisheddoingstuff(): keeplooping=False
Run Code Online (Sandbox Code Playgroud)

或类似的东西.你可以在内部循环中设置一个变量,并在内部循环退出后立即在外部循环中检查它,如果合适则中断.我有点像GOTO方法,只要你不介意使用愚人节的笑话模块 - 它不是Pythonic,但它确实有意义.


Jas*_*ker 12

这不是最漂亮的方式,但在我看来,这是最好的方式.

def loop():
    while True:
    #snip: print out current state
        while True:
            ok = get_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": return
            if ok == "n" or ok == "N": break
        #do more processing with menus and stuff
Run Code Online (Sandbox Code Playgroud)

我很确定你也可以在这里使用递归来解决问题,但我不知道这对你来说是个不错的选择.


Muh*_*eed 12

从语言层面来说是没有办法做到这一点的。有些语言有 goto,其他语言有带参数的中断,但 python 没有。

最好的选择是:

  1. 设置一个由外循环检查的标志,或设置外循环条件。

  2. 将循环放入函数中,然后使用 return 立即跳出所有循环。

  3. 重新表述你的逻辑。

信用


使用功能

def doMywork(data):
    for i in data:
       for e in i:
         return 
Run Code Online (Sandbox Code Playgroud)

使用标志

is_break = False
for i in data:
   if is_break:
      break # outer loop break
   for e in i:
      is_break = True
      break # inner loop break
Run Code Online (Sandbox Code Playgroud)


Mau*_*spé 9

如果两个条件成立,为什么不保持循环?我认为这是一种更加pythonic的方式:

dejaVu = True

while dejaVu:
    while True:
        ok = raw_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y" or ok == "n" or ok == "N":
            dejaVu = False
            break
Run Code Online (Sandbox Code Playgroud)

不是吗?

祝一切顺利.

  • @MatthewScharley我认为这是为了表明它在嵌套循环中有效。 (2认同)

小智 8

将循环逻辑分解为一个迭代器,它产生循环变量并在完成时返回 - 这是一个简单的循环变量,它在行/列中排列图像,直到我们没有图像或者放置它们的地方:

def it(rows, cols, images):
    i = 0
    for r in xrange(rows):
        for c in xrange(cols):
            if i >= len(images):
                return
            yield r, c, images[i]
            i += 1 

for r, c, image in it(rows=4, cols=4, images=['a.jpg', 'b.jpg', 'c.jpg']):
    ... do something with r, c, image ...
Run Code Online (Sandbox Code Playgroud)

这有利于分割复杂的循环逻辑和处理......


one*_*ion 6

将多个循环转变为单个可破坏循环的简单方法是使用numpy.ndindex

for i in range(n):
  for j in range(n):
    val = x[i, j]
    break # still inside the outer loop!

for i, j in np.ndindex(n, n):
  val = x[i, j]
  break # you left the only loop there was!
Run Code Online (Sandbox Code Playgroud)

您确实必须对对象进行索引,而不是能够显式地迭代值,但至少在简单的情况下,它似乎比大多数建议的答案简单大约 2-20 倍。


hol*_*roy 5

Python 结构中有一个隐藏的技巧while ... else,可以用来模拟双中断,而无需更改/添加太多代码。本质上,如果while条件为假,else则触发块。既不异常,continue也不break触发else块。有关更多信息,请参阅“ Python while 语句上的 Else 子句”的答案,或while (v2.7) 上的 Python 文档

while True:
    #snip: print out current state
    ok = ""
    while ok != "y" and ok != "n":
        ok = get_input("Is this ok? (y/n)")
        if ok == "n" or ok == "N":
            break    # Breaks out of inner loop, skipping else

    else:
        break        # Breaks out of outer loop

    #do more processing with menus and stuff
Run Code Online (Sandbox Code Playgroud)

唯一的缺点是您需要将双重中断条件移至条件中while(或添加标志变量)。循环也存在这种变化for,其中else块在循环完成后被触发。