如何跳出多个循环?

723

给定以下代码(无法正常工作):

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

有没有办法让这个工作?还是我必须进行一次检查以跳出输入循环,然后在外部循环中进行另一个更有限的检查,以便在用户满意时一起退出?

193
为什么Python不能只有一个'break(n)',其中n是你想要跳出的层数? - Nathan
14
如果你嵌套了很多循环,使用C++的goto语句会非常方便。 - Drake Johnson
2
@Nathan 看看这个问题:为什么Python不原生支持像goto这样的关键字来跳出n个循环,Nathan给出了一个非常好的解释。 - Shivam Jha
3
Ned Batchelder有一个有趣的演讲,解决了“如何打破两个循环”的问题。剧透警告:将双重循环变为单一循环。 - Tiago Martins Peres
将循环放在try except块中,并通过引发异常来跳出循环。在我看来,这是最易读的方法。 - undefined
39个回答

0

标记您可以使用一个标记来跳出循环:

if found:
  break

这里,“found”是标志,您最初将其设置为False,然后在循环中使用此代码。
found = False
for table_height in range(500):
  if found:
    break

这是带有三个for循环的完整代码:

found = False
for table_height in range(500):
  if found:
    break
  for cat_height in range(500):
    if found:
      break

    for tort_height in range(500):
      equation1 = table_height + cat_height == tort_height + 170
      equation2 = table_height + tort_height == cat_height + 130
      if equation1 and equation2:
        print('table', table_height, '  cat', cat_height, '  tortoise', tort_height)
        found = True
        break

在这段代码中,如果 equation1 和 equation2 都为 True,它将把“found”标志设置为 True,并跳出最内层的 for 循环,同时也会跳出其他两个 for 循环,因为“found”为 True。

0

如果你只是需要在一个复杂的嵌套for循环中测试一个边缘情况,你可以插入一个1/0来引发异常。我保证不会告诉任何人。当你想要快速测试一个深度嵌套的for循环的单个迭代时,这非常方便,而且你不想追踪大量的break语句或注释掉大量的代码。

是的,你可以将其包装在一个函数中并使用return,但在某些情况下,这可能过于繁琐。

入门级程序员的示例:

for i in first_iter:
    for j in second_iter:
        for k in third_iter:
            print(i_want_to_run_this_once_and_stop_executing(i,j,k))
            1/0
        code_that_takes_a_long_time()
    expensive_code()

在大型的Jupyter Notebook脚本中进行一些繁重的数据预处理时,这将非常方便。

0
while True:
    # Snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok.lower() == "y": 
            break_2 = True
        if ok.lower() == "n": 
            break
    if break_2:
        break

0
尝试对原帖的问题进行最小化的更改,我只是在第一个for循环之前添加了一个标志,并在外部循环中检查该标志,以确定我们是否需要再次中断。
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

你能描述一下你改了什么吗?你的想法/主旨是什么?根据帮助中心的说法:“...始终要解释为什么你提出的解决方案是合适的以及它是如何工作的”。请通过编辑(更改)您的答案来回应,而不是在评论中回复(不要添加“编辑:”,“更新:”或类似内容 - 答案应该看起来像是今天写的)。 - Peter Mortensen

0
这是一个非常简短的版本: 创建一个名为break_out_nested.py的文件。
import itertools
import sys

it = sys.modules[__name__] # this allows us to share variables with break_out_nested.py when we import it 


def bol(*args):
    condi = args[-1] # the condition function
    i = args[:-1] # all iterables 
    for p in itertools.product(*i): # itertools.product creates the nested loop
        if condi(): # if the condition is True, we return 
            return
        yield p # if not, we yield the result 

现在你只需要几行代码就可以跳出嵌套循环(来自Rafiq示例的数据)

from break_out_nested import it, bol # import what we have just created

# you need to create new variables as attributes of it,
# because break_out_nested has only access to these variables
it.i, it.j, it.k = 1, 1, 1
# the break condition
def cond(): return it.i % 3 == 0 and it.j % 3 == 0 and it.k % 3 == 0

# The condition will be checked in each loop 
for it.i, it.j, it.k in bol(range(1, 6, 1), range(1, 11, 2, ), range(1, 21, 4), cond):
    print(it.i, it.j, it.k)

更多示例:

def cond(): return it.i + it.j + it.k == 777

it.i, it.j, it.k = 0, 0, 0
for it.i, it.j, it.k in bol(range(100), range(1000), range(10000), cond):
    print(it.i, it.j, it.k)




def cond(): return it.i + it.j + it.k >= 100000

it.i, it.j, it.k = 0, 0, 0
# you dont have to use it.i, it.j, it.k as the loop variables, you can
# use anything you want, but you have to update the variables somewhere
for i, j, k in bol(range(100), range(1000), range(10000), cond):
    it.i, it.j, it.k = i * 10, j * 100, k * 100
    print(it.i, it.j, it.k)

-1

break用于退出外层和内层while循环:

while True:
    while True:
        print('Breaks inner "while" loop')
        break # Here
    print('Breaks outer "while" loop')
    break # Here

或者,使用带有if语句的外部和内部while循环的break

while True:
    while True:
        if True:
            print('Breaks inner "while" loop')
            break # Here
    print('Breaks outer "while" loop')
    break # Here

输出:

Breaks inner "while" loop
Breaks outer "while" loop

break 用于跳出外层和内层的 for 循环:

for _ in iter(int, 1):
    for _ in iter(int, 1):
        print('Breaks inner "for" loop')
        break # Here
    print('Breaks outer "for" loop')
    break # Here

或者,使用带有if语句的外部和内部for循环中的break

for _ in iter(int, 1):
    for _ in iter(int, 1):
        if True:
            print('Breaks inner "for" loop')
            break # Here
    print('Breaks outer "for" loop')
    break # Here

输出:

Breaks inner "for" loop
Breaks outer "for" loop

-3

由于这个问题已经成为进入特定循环的标准问题,我想用Exception的例子来回答。

虽然在多重循环结构中不存在名为“breaking of loop”的标签,但我们可以利用用户定义的异常来打破我们选择的特定循环。考虑以下示例,在其中让我们打印出基于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)进入第一个循环。

简而言之,在内部循环中引发您的异常(内置或用户定义),并在您希望恢复控制的循环中捕获它。如果要从所有循环中退出,请捕获超出所有循环的异常。(我没有在示例中显示此情况)。


关于“breaking into”:你是不是指“breaking out of”? - Peter Mortensen

-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   # <--- !

2
这看起来相当丑陋,使代码比之前更难理解。而且它是错误的。它忽略了实际检查输入是否可接受,并在1个循环后中断。 - Eric
哪一个是之前的?它指的是特定的答案吗? - Peter Mortensen

-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!''')

这提供了很多控制程序如何中断的方式,允许您选择何时中断以及要向下走多少级。


4
使用正确的布尔值 TrueFalse 至少可以将这个代码从糟糕变为仅仅不太美观。 - tripleee

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