我如何打破多个循环?

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

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

有没有办法让这个工作?或者我必须做一次检查来打破输入循环,然后再做一次更有限的检查,如果用户满意,在外循环中检查以一起打破所有循环?

663060 次浏览

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

如果,由于某种原因,不能制定终止条件,例外是一个后备计划。

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”选项,我们希望立即退出。这更特殊。

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

您还可以通过以下方式使用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”等等,但它在这样的奇怪情况下效果很好。

我的第一反应是将嵌套循环重构为一个函数并使用return来打破。

keeplooping = True
while keeplooping:
# Do stuff
while keeplooping:
# Do some other stuff
if finisheddoingstuff():
keeplooping = False

或者类似的东西。

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

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

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

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

PEP 3136提出了标记为关掉/继续的方案。Guido拒绝了它是因为“需要这个功能的代码非常复杂”。不过,PEP确实提到了一些变通方法(例如异常技术),而Guido觉得重构使用返回在大多数情况下会更简单。

将循环逻辑考虑到一个迭代器中,该迭代器产生循环变量并在完成后返回-这里有一个简单的方法,它将图像按行/列排列,直到我们没有图像或没有地方放置它们:

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 ...

这具有将复杂的循环逻辑和处理分离的优点…

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

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

这使用了在为什么python在for和而循环之后使用“其他”?中解释的for/else构造

关键见解:只有似乎就好像外部循环总是中断一样。但是如果内部循环不中断,外部循环也不会中断。

continue语句在这里很神奇。它在for-else子句中。根据定义发生在没有内部中断的情况下。在这种情况下,continue巧妙地绕过了外部中断。

我倾向于同意重构为函数通常是这种情况的最佳方法,但是当你真的需要打破嵌套循环时,这里有一个@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"

优点:(1)它稍微干净一些(没有显式的try-除了块),(2)每次使用nested_break都会得到一个定制的Exception子类;每次都不需要声明自己的Exception子类。

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

如果你有一个无限循环,这是唯一的出路;对于其他循环,执行速度真的要快得多。如果你有很多嵌套循环,这也有效。你可以退出所有,或者只是几个。无限的可能性!希望这有帮助!

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

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

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

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问题的一个具体案例,但我发布它是希望它能帮助人们以不同的方式思考他们的问题,同时保持简单。

如果两个条件为真,则继续循环。

我认为这是一种更加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

尝试使用无限生成器。

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

在这种情况下,正如其他人所指出的那样,函数分解是要走的路。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

Pythonwhile ... else结构中有一个隐藏的技巧,可以用来模拟双中断,而无需太多代码更改/添加。本质上,如果while条件为false,则触发else块。continuebreak都没有触发else块。有关更多信息,请参阅“Python同时语句上的其他子句”或Python doc on这时候(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块在循环完成后触发。

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

添加1break_level变量来控制这时候循环条件

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

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

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

您可以将其扩展到循环的任何级别

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

另一个缺点是它不适用于同时循环。我最初想在Python-“打破”所有循环上发布这个答案,但不幸的是,它作为这个的副本关闭了

您可以定义一个变量(例如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

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

因此,您可以将函数用作“可破坏的控制结构”,定义要返回的位置:

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

希望这有助于:

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

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

尽管在多环构造中不存在名为打破循环的标签,但我们可以使用用户自定义异常来打破我们选择的特定循环。考虑以下示例,让我们在以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

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

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

要在不重构为函数的情况下打破多个嵌套循环,请使用内置停止迭代异常的“模拟goto语句”:

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


except StopIteration: pass

请参阅这次讨论关于使用goto语句断开嵌套循环。

我解决这个问题的方法是定义一个变量,该变量被引用来确定您是否中断到下一个级别。在这个例子中,这个变量被称为“应该中断”。

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

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

使用函数:

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的代码。

使用 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倍。

解决方法有两种

举个例子: 这两个矩阵相等/相同吗?
矩阵1和矩阵2是相同大小的,n,二维矩阵。

第一个没有函数的解 ,< em >

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

带函数的第二个解 ,< em >

这是我案子的最终解决方案。

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

下面是一个看起来有效的实现方法:

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_

从语言层面上是没有办法做到这一点的。有些语言 一个 goto 别人有一个休息,采取一个论点,巨蟒没有。

最好的选择是:

  1. 设置一个由外部循环检查的标志,或者设置外部 环状态

  2. 将循环放入一个函数中,并使用 return 一次性打破所有循环。

  3. 重新组织你的逻辑。

荣誉归于 Vivek Nagarajan,自1987年以来的程序员


使用函数

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

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

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

尝试 最小化改变 OP 的问题,我只是在打破第一个 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

我最近偶然发现了这个问题,为了避免重复返回语句(这可能会掩盖逻辑错误) ,我查看了@yak 的想法。这在嵌套的 for 循环中可以很好地工作,但是并不十分优雅。另一种方法是在下一个循环之前检查条件:

b = None
for a in range(10):
if something(a, b): # should never = True if b is None
break
for b in range(20):
pass

这可能不是在所有地方都适用,但是具有适应性,如果需要,还具有允许条件重复而不是潜在结果的优点。

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

如果您只是需要在复杂的 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()

在大型木星笔记本脚本做一些沉重的数据预处理,这是特别方便。

while外环和内环的 break:

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

for外环和内环的 break:

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