642

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

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

有没有办法使这项工作?或者我是否需要进行一次检查以跳出输入循环,然后进行另一项更有限的检查,以在用户满意的情况下一起跳出外部循环?

4

33 回答 33

655

我的第一直觉是将嵌套循环重构为一个函数并用于return中断。

于 2008-10-10T00:25:05.480 回答
400

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

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

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

关键见解:似乎外循环似乎总是中断。但是如果内循环没有中断,外循环也不会。

continue声明是这里的魔力。它在 for-else 子句中。根据定义,如果没有内部中断,就会发生这种情况。在那种情况下continue巧妙地规避了外部中断。

于 2010-06-30T14:15:59.533 回答
170

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

于 2008-10-10T03:50:44.317 回答
145

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

如果由于某种原因无法确定终止条件,则例外是一种备用计划。

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

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

另一方面,我们在字符模式应用程序中经常有“Y”、“N”和“Q”选项。对于“Q”选项,我们希望立即退出。那更不寻常。

于 2008-10-10T00:11:37.910 回答
60

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

from contextlib import contextmanager
@contextmanager
def nested_break():
    class NestedBreakException(Exception):
        pass
    try:
        yield NestedBreakException
    except NestedBreakException:
        pass

现在您可以按如下方式使用此上下文管理器:

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"

Exception优点:(1)它更简洁(没有显式的 try-except 块),以及(2)每次使用nested_break;你都会得到一个定制的子类。无需Exception每次都声明自己的子类。

于 2010-07-03T15:50:53.507 回答
55

引入一个您将用作“循环断路器”的新变量。首先为它分配一些东西(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   # <--- !

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

于 2011-07-03T18:15:55.093 回答
46

首先,您还可以考虑将获取和验证输入的过程变成一个函数;在该函数中,如果它正确,您可以只返回该值,如果不正确,则继续在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

我知道,我知道,“你不能使用 goto”等等,但它在像这样的奇怪情况下效果很好。

于 2008-10-10T00:12:38.533 回答
35

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

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

except StopIteration: pass

请参阅有关使用 goto 语句打破嵌套循环的讨论。

于 2017-10-28T00:52:41.437 回答
20
保持循环=真
同时保持循环:
    #做东西
    同时保持循环:
          #做一些其他的事情
          如果完成了doingstuff():
              保持循环=假

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

于 2008-10-10T00:29:37.640 回答
13

这不是最漂亮的方法,但在我看来,这是最好的方法。

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

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

于 2008-10-10T01:41:42.643 回答
11

如果两个条件都为真,为什么不继续循环呢?我认为这是一种更 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

不是吗?

祝一切顺利。

于 2012-11-06T14:02:32.670 回答
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 ...

这样做的好处是将复杂的循环逻辑和处理...

于 2010-04-12T11:33:15.763 回答
6

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

最好的选择是:

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

  2. 将循环放在一个函数中,并使用 return 一次跳出所有循环。

  3. 重新制定你的逻辑。

归功于自 1987 年以来的程序员 Vivek Nagarajan


使用功能

def doMywork(data):
    for i in data:
       for e in i:
         return 

使用标志

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
于 2020-05-22T05:38:20.853 回答
4

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

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

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

于 2015-10-14T09:29:41.420 回答
4

将多个循环变成一个可破坏循环的简单方法是使用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!

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

于 2018-05-12T19:55:43.807 回答
3

在这种情况下,正如其他人所指出的那样,功能分解是要走的路。Python 3 中的代码:

def user_confirms():
    while True:
        answer = input("Is this OK? (y/n) ").strip().lower()
        if answer in "yn":
            return answer == "y"

def main():
    while True:
        # do stuff
        if user_confirms():
            break
于 2015-08-15T08:42:47.873 回答
3

将迭代减少到单级循环的另一种方法是使用生成器,这也在python 参考中指定

for i, j in ((i, j) for i in A for j in B):
    print(i , j)
    if (some_condition):
        break

您可以将其扩展到循环的任意数量的级别

缺点是你不能再只打破一个级别。要么全有,要么全无。

另一个缺点是它不适用于 while 循环。我最初想在Python 上发布这个答案 - `break` out of all loops但不幸的是,它作为这个的副本关闭了

于 2016-12-01T18:10:03.337 回答
3

我想提醒您,Python 中的函数可以在代码中间创建,并且可以透明地访问周围的变量以进行读取和 withnonlocalglobal声明以进行写入。

所以你可以使用一个函数作为一个“可破坏的控制结构”,定义一个你想要返回的地方:

def is_prime(number):

    foo = bar = number

    def return_here():
        nonlocal foo, bar
        init_bar = bar
        while foo > 0:
            bar = init_bar
            while bar >= foo:
                if foo*bar == number:
                    return
                bar -= 1
            foo -= 1

    return_here()

    if foo == 1:
        print(number, 'is prime')
    else:
        print(number, '=', bar, '*', foo)

>>> is_prime(67)
67 is prime
>>> is_prime(117)
117 = 13 * 9
>>> is_prime(16)
16 = 4 * 4
于 2017-06-13T09:26:52.737 回答
3

通过使用函数:

def myloop():
    for i in range(1,6,1):  # 1st loop
        print('i:',i)
        for j in range(1,11,2):  # 2nd loop
            print('   i, j:' ,i, j)
            for k in range(1,21,4):  # 3rd loop
                print('      i,j,k:', i,j,k)
                if i%3==0 and j%3==0 and k%3==0:
                    return  # getting out of all loops

myloop()

尝试通过注释掉上面的代码来运行return

不使用任何功能:

done = False
for i in range(1,6,1):  # 1st loop
    print('i:', i)
    for j in range(1,11,2):  # 2nd loop
        print('   i, j:' ,i, j)
        for k in range(1,21,4):  # 3rd loop
            print('      i,j,k:', i,j,k)
            if i%3==0 and j%3==0 and k%3==0:
                done = True
                break  # breaking from 3rd loop
        if done: break # breaking from 2nd loop
    if done: break     # breaking from 1st loop

现在,首先按原样运行上述代码,然后尝试通过注释掉break从底部开始一次包含一个的每一行来运行。

于 2018-04-05T03:59:37.563 回答
2

我来这里的原因是我有一个外循环和一个内循环,如下所示:

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

  do some other stuff with x

如您所见,它实际上不会转到下一个 x,而是会转到下一个 y。

我发现解决这个问题的方法只是遍历数组两次:

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

for x in array:
  do some other stuff with x

我知道这是 OP 问题的一个具体案例,但我发布它是希望它能帮助人们以不同的方式思考他们的问题,同时保持简单。

于 2012-08-15T18:12:49.110 回答
2

尝试使用无限生成器。

from itertools import repeat
inputs = (get_input("Is this ok? (y/n)") for _ in repeat(None))
response = (i.lower()=="y" for i in inputs if i.lower() in ("y", "n"))

while True:
    #snip: print out current state
    if next(response):
        break
    #do more processing with menus and stuff
于 2013-03-21T22:45:58.040 回答
2
# this version uses a level counter to choose how far to break out

break_levels = 0
while True:
    # snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y":
            break_levels = 1        # how far nested, excluding this break
            break
        if ok == "n" or ok == "N":
            break                   # normal break
    if break_levels:
        break_levels -= 1
        break                       # pop another level
if break_levels:
    break_levels -= 1
    break

# ...and so on
于 2013-06-13T16:53:56.533 回答
2
# this version breaks up to a certain label

break_label = None
while True:
    # snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y":
            break_label = "outer"   # specify label to break to
            break
        if ok == "n" or ok == "N":
            break
    if break_label:
        if break_label != "inner":
            break                   # propagate up
        break_label = None          # we have arrived!
if break_label:
    if break_label != "outer":
        break                       # propagate up
    break_label = None              # we have arrived!

#do more processing with menus and stuff
于 2013-06-13T17:15:18.810 回答
2

这是一个似乎可行的实现:

break_ = False
for i in range(10):
    if break_:
        break
    for j in range(10):
        if j == 3:
            break_ = True
            break
        else:
            print(i, j)

唯一的缺点是您必须break_在循环之前定义。

于 2020-05-02T15:58:52.613 回答
1

如果不喜欢重构为函数,可能像下面这样的小技巧会做

添加了 1 个 break_level 变量来控制 while 循环条件

break_level = 0
# while break_level < 3: # if we have another level of nested loop here
while break_level < 2:
    #snip: print out current state
    while break_level < 1:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y": break_level = 2 # break 2 level
        if ok == "n" or ok == "N": break_level = 1 # break 1 level
于 2016-10-19T01:44:36.907 回答
1

您可以定义一个变量(例如break_statement),然后在发生两次中断条件时将其更改为不同的值,并在 if 语句中使用它来中断第二个循环。

while True:
    break_statement=0
    while True:
        ok = raw_input("Is this ok? (y/n)")
        if ok == "n" or ok == "N": 
            break
        if ok == "y" or ok == "Y": 
            break_statement=1
            break
    if break_statement==1:
        break
于 2017-03-22T08:56:44.930 回答
1

两种方式的解决方案

举个例子:这两个矩阵是否相等/相同?
matrix1 和 matrix2 是相同大小、n、2 维矩阵。

第一个解决方案没有功能

same_matrices = True
inner_loop_broken_once = False
n = len(matrix1)

for i in range(n):
    for j in range(n):

        if matrix1[i][j] != matrix2[i][j]:
            same_matrices = False
            inner_loop_broken_once = True
            break

    if inner_loop_broken_once:
        break

第二个解决方案有一个功能
这是我的案例的最终解决方案

def are_two_matrices_the_same (matrix1, matrix2):
    n = len(matrix1)
    for i in range(n):
        for j in range(n):
            if matrix1[i][j] != matrix2[i][j]:
                return False
    return True

祝你今天过得愉快!

于 2019-03-05T11:44:35.987 回答
1

我个人会做的是使用一个布尔值,当我准备好打破外循环时切换。例如

while True:
    #snip: print out current state
    quit = False
    while True:
        ok = input("Is this ok? (y/n)")
        if ok.lower() == "y":
            quit = True
            break # this should work now :-)
        if ok.lower() == "n":
            quit = True
            break # This should work too :-)
    if quit:
        break
    #do more processing with menus and stuff
于 2021-03-11T17:25:37.713 回答
0

希望这会有所帮助:

x = True
y = True
while x == True:
    while y == True:
         ok = get_input("Is this ok? (y/n)") 
         if ok == "y" or ok == "Y":
             x,y = False,False #breaks from both loops
         if ok == "n" or ok == "N": 
             break #breaks from just one
于 2017-07-26T13:14:33.007 回答
0

对 OP 问题的改动很小。

break_2 = False
while True:
    #snip: print out current state
    if break_2: break
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok.lower() == "y": break_2 = True
        if break_2: break
        if ok.lower() == "n": break
    #do more processing with menus and stuff
于 2022-01-08T20:43:22.837 回答
-3

与之前的类似,但更紧凑。(布尔值只是数字)

breaker = False #our mighty loop exiter!
while True:
    while True:
        ok = get_input("Is this ok? (y/n)")
        breaker+= (ok.lower() == "y")
        break

    if breaker: # the interesting part!
        break   # <--- !
于 2012-06-07T11:12:22.363 回答
-3

由于这个问题已成为进入特定循环的标准问题,因此我想以使用Exception.

尽管在多重循环结构中不存在名为“循环中断”的标签,但我们可以利用用户定义的异常来中断我们选择的特定循环。考虑以下示例,让我们在 base-6 编号系统中打印最多 4 位的所有数字:

class BreakLoop(Exception):
    def __init__(self, counter):
        Exception.__init__(self, 'Exception 1')
        self.counter = counter

for counter1 in range(6):   # Make it 1000
    try:
        thousand = counter1 * 1000
        for counter2 in range(6):  # Make it 100
            try:
                hundred = counter2 * 100
                for counter3 in range(6): # Make it 10
                    try:
                        ten = counter3 * 10
                        for counter4 in range(6):
                            try:
                                unit = counter4
                                value = thousand + hundred + ten + unit
                                if unit == 4 :
                                    raise BreakLoop(4) # Don't break from loop
                                if ten == 30: 
                                    raise BreakLoop(3) # Break into loop 3
                                if hundred == 500:
                                    raise BreakLoop(2) # Break into loop 2
                                if thousand == 2000:
                                    raise BreakLoop(1) # Break into loop 1

                                print('{:04d}'.format(value))
                            except BreakLoop as bl:
                                if bl.counter != 4:
                                    raise bl
                    except BreakLoop as bl:
                        if bl.counter != 3:
                            raise bl
            except BreakLoop as bl:
                if bl.counter != 2:
                    raise bl
    except BreakLoop as bl:
        pass

当我们打印输出时,我们永远不会得到任何单位位为 4 的值。在这种情况下,我们不会从任何循环中中断,因为BreakLoop(4)在同一个循环中引发并捕获。类似地,每当十位有 3 时,我们使用 . 进入第三个循环BreakLoop(3)。每当百位有 5 时,我们使用 进入第二个循环BreakLoop(2),而当千位有 2 时,我们使用 进入第一个循环BreakLoop(1)

简而言之,在内部循环中引发您的异常(内置或用户定义),并在您想要恢复控制的循环中捕获它。如果要中断所有循环,请在所有循环之外捕获异常。(我没有在示例中展示这种情况)。

于 2017-10-23T12:26:51.033 回答
-4

我解决这个问题的方法是定义一个引用的变量来确定你是否突破到下一个级别。在此示例中,此变量称为“shouldbreak”。

Variable_That_Counts_To_Three=1
while 1==1:
    shouldbreak='no'
    Variable_That_Counts_To_Five=0
    while 2==2:
        Variable_That_Counts_To_Five+=1
        print(Variable_That_Counts_To_Five)
        if Variable_That_Counts_To_Five == 5:
            if Variable_That_Counts_To_Three == 3:
                shouldbreak='yes'
            break
    print('Three Counter = ' + str(Variable_That_Counts_To_Three))
    Variable_That_Counts_To_Three+=1
    if shouldbreak == 'yes':
        break

print('''
This breaks out of two loops!''')

这可以很好地控制您希望程序中断的准确程度,允许您选择何时中断以及下降多少级别。

于 2018-03-06T17:12:51.307 回答