How to break out of multiple loops in Python?
给定以下代码(不起作用):
1 2 3 4 5 6 7 | 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 |
有什么方法可以让这个工作吗?或者,如果用户满意,我是否需要执行一次检查以断开输入循环,然后执行另一个更有限的检查以断开外部循环?
我的第一直觉是将嵌套循环重构为一个函数,并使用
这是另一个简短的方法。缺点是你只能打破外环,但有时这正是你想要的。
1 2 3 4 5 6 7 8 9 10 | 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构造,解释于:为什么在for和while循环之后使用"else"?
关键洞察:似乎只有外环总是断裂。但如果内环不断裂,外环也不会断裂。
江户十一〔一〕的说法是这里的魔法。它在for else条款中。根据定义,如果没有内部破裂,就会发生这种情况。在这种情况下,
PEP 3136建议贴上"中断/继续"标签。guido拒绝了它,因为"如此复杂的代码需要这个特性是非常罕见的"。不过,PEP确实提到了一些解决方法(例如异常技术),而Guido认为,在大多数情况下,使用返回进行重构会更简单。
首先,普通逻辑是有用的。
如果由于某种原因,终止条件不能得到解决,例外情况就是一个倒退计划。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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"选项,我们希望立即退出。这更为特殊。
对于这种情况,我倾向于认为重构为一个函数通常是最好的方法,但是对于真正需要突破嵌套循环的情况,这里有一个有趣的变体,即@s.lott所描述的异常引发方法。它使用python的
1 2 3 4 5 6 7 8 9 | from contextlib import contextmanager @contextmanager def nested_break(): class NestedBreakException(Exception): pass try: yield NestedBreakException except NestedBreakException: pass |
现在,您可以按如下方式使用此上下文管理器:
1 2 3 4 5 6 7 8 | 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)比较干净(除了块外没有明确的尝试),(2)每次使用
首先,您还可以考虑将获取和验证输入的过程设为一个函数;在该函数中,如果值正确,您只需返回该值,如果不正确,则在while循环中继续旋转。这从本质上消除了您解决的问题,通常可以应用于更一般的情况(突破多个循环)。如果您必须在代码中保留此结构,并且确实不想处理簿记布尔值…
您也可以以下方式使用goto(使用这里的april-fouls模块):
1 2 3 4 5 6 7 8 9 10 11 | #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"和所有这些,但它在这种奇怪的情况下工作得很好。
引入一个新变量,用作"循环断路器"。首先给它赋值(false、0等),然后在外部循环中,在中断之前,将值更改为其他值(true、1等)。一旦循环退出,就对该值进行"父"循环检查。让我演示一下:
1 2 3 4 5 6 7 8 9 | breaker = False #our mighty loop exiter! while True: while True: if conditionMet: #insert code here... breaker = True break if breaker: # the interesting part! break # <--- ! |
如果有一个无限循环,这是唯一的出路;因为其他循环的执行速度确实要快得多。如果有许多嵌套循环,这也可以工作。你可以全部退出,或者只退出几个。无限可能!希望这有帮助!
1 2 3 4 5 6 | keeplooping=True while keeplooping: #Do Stuff while keeplooping: #do some other stuff if finisheddoingstuff(): keeplooping=False |
或者类似的。您可以在内部循环中设置一个变量,并在内部循环退出后立即在外部循环中对其进行检查,如果合适,可以中断。我有点喜欢goto方法,只要你不介意使用愚人节的笑话模块——它不是Python式的,但它确实有意义。
要在不重构为函数的情况下突破多个嵌套循环,请使用带有内置stopIteration异常的"模拟goto语句":
1 2 3 4 5 6 7 | try: for outer in range(100): for inner in range(100): if break_early(): raise StopIteration except StopIteration: pass |
请参阅关于使用goto语句分解嵌套循环的讨论。
这不是最漂亮的方法,但在我看来,这是最好的方法。
1 2 3 4 5 6 7 8 | 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 |
我很确定你也可以用递归来解决问题,但我不知道这是否是你的一个好选择。
如果两个条件都成立,为什么不继续循环呢?我认为这是一种更像Python的方式:
1 2 3 4 5 6 7 8 | 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 |
不是吗?
祝你一切顺利。
将循环逻辑分解为一个迭代器,该迭代器生成循环变量并在完成时返回——这里是一个简单的循环逻辑,它以行/列的形式排列图像,直到图像用完或放不下为止:
1 2 3 4 5 6 7 8 9 10 11 | 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 ... |
它的优点是将复杂的循环逻辑和处理过程分开。
python
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
唯一的缺点是需要将双中断条件移动到
在这种情况下,正如其他人所指出的,功能分解是一种可行的方法。python 3中的代码:
1 2 3 4 5 6 7 8 9 10 11 | 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 |
我来这里的原因是我有一个外环和一个内环,就像这样:
1 2 3 4 5 6 7 | 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。
我发现解决这个问题的方法只是在数组中运行两次:
1 2 3 4 5 6 7 8 | 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问题的一个具体案例,但我张贴它的目的是希望它能帮助人们在保持简单的同时,以不同的方式思考他们的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |
通过使用函数:
1 2 3 4 5 6 7 8 9 10 11 | 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() |
试着通过注释
不使用任何功能:
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
现在,首先按原样运行上述代码,然后尝试运行,从底部逐个注释包含
另一种将迭代减少到单级循环的方法是使用在python参考中指定的生成器。
1 2 3 4 | for i, j in ((i, j) for i in A for j in B): print(i , j) if (some_condition): break |
您可以将其扩展到循环的任意级别。
缺点是你不能再只打破一个水平。要么全部要么什么都没有。
另一个缺点是它不适用于while循环。我本来想在python上发布这个答案——打破所有的循环,但不幸的是,它是作为这个循环的副本关闭的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |
如果不喜欢重构成函数,下面这样的小技巧可能就不会有了。
添加了1个break_级别变量来控制while循环条件
1 2 3 4 5 6 7 8 | 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 |
您可以定义一个变量(例如break_语句),然后在出现两个break条件时将其更改为不同的值,并在if语句中使用它来从第二个循环中断。
1 2 3 4 5 6 7 8 9 10 11 | 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 |
将多个循环转换为单个可断开循环的一种简单方法是使用
1 2 3 4 5 6 7 8 | 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 3 4 5 6 7 8 9 | 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 |
解决方案有两种方式
举个例子:这两个矩阵相等吗?< BR>Matrix1和Matrix2大小相同,n,2维矩阵。
第一个解决方案,不带函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
第二个解,带函数
这是我的案例的最终解决方案
1 2 3 4 5 6 7 | 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 |
祝你今天愉快!
我想提醒您,python中的函数可以在代码的正中间创建,并且可以透明地访问周围的变量进行读取,并使用
因此,可以将函数用作"可断开的控制结构",定义要返回的位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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) |
1 2 3 4 5 6 | >>> is_prime(67) 67 is prime >>> is_prime(117) 117 = 13 * 9 >>> is_prime(16) 16 = 4 * 4 |
希望这有助于:
1 2 3 4 5 6 7 8 9 | 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 |
和以前一样,但更紧凑。(布尔值就是数字)
1 2 3 4 5 6 7 8 9 | 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 # <--- ! |
由于这个问题已经成为进入特定循环的标准问题,我想用
虽然在多环结构中不存在名为breaking of loop的标签,但是我们可以使用用户定义的异常来中断我们选择的特定循环。请考虑以下示例,其中让我们在Base-6编号系统中打印最多4个数字:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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的任何值。在这种情况下,我们不会从任何循环中断,因为
简而言之,在内部循环中引发异常(内置的或用户定义的),并在循环中捕获它,从中恢复控件。如果要从所有循环中中断,请在所有循环之外捕获异常。(我没有在示例中显示这个案例)。
我解决这个问题的方法是定义一个变量,该变量被引用来确定是否要进入下一个级别。在本例中,此变量称为"shouldbreak"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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!''') |
这可以很好地控制程序的中断方式,允许您选择何时中断以及要降低多少级别。