如何摆脱Python中的多个循环?

鉴于下面的代码(这是行不通的):

while True: #snip: print out current state while True: ok = get_input("Is this ok? (y/n)") if ok == "y" or ok == "Y": break 2 #this doesn't work :( if ok == "n" or ok == "N": break #do more processing with menus and stuff 

有没有办法做到这一点? 或者我有做一个检查打破input循环,然后另一个更有限的,检查外部循环打破了一起,如果用户满意?

Edit-FYI: get_input是我写的一个简短的函数,它支持显示提示和默认值以及所有的stdin.readline().strip() ,并返回stdin.readline().strip()

我的第一个直觉是将嵌套循环重构成一个函数,并使用return突破。

PEP 3136build议标记为break / continue。 Guido 拒绝了,因为“代码如此复杂以至于不需要这个特性”是非常罕见的。 虽然PEP确实提到了一些解决方法(例如exception技术),但是在大多数情况下,Guido认为重构使用return方法会更简单。

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

 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 

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

如果由于某种原因终止条件无法制定出来,例外是一个倒退计划。

 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描述的exception提升方法的一个有趣的变体。 它使用Python的声明使exception提升看起来更好一些。 定义一个新的上下文pipe理器(你只需要做一次):

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

现在你可以使用这个上下文pipe理器如下:

 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-except块),(2)每次使用nested_break都得到一个自定义的Exception子类; 无需每次都声明自己的Exception子类。

首先,你也可以考虑进行获取和validationinput函数的过程; 在那个函数中,如果它的值正确,你可以返回值,如果没有的话,在while循环中继续旋转。 这基本上消除了你解决的问题,通常可以应用在更一般的情况下(突破多个循环)。 如果你绝对必须在你的代码中保持这个结构,并且真的不想处理记帐布尔值…

您也可以按照以下方式使用goto (从这里使用April Fools模块):

 #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”等等,但是在这样的奇怪的情况下,它运行的很好。

引入一个新variables,您将用作“断路器”。 首先给它分配一些东西(False,0等),然后,在外部循环内部,在你断开之前,把值改为别的东西(True,1,…)。 一旦循环退出,使“父”循环检查该值。 让我来certificate一下:

 breaker = False #our mighty loop exiter! while True: while True: if conditionMet: #insert code here... breaker = True break if breaker: # the interesting part! break # <--- ! 

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

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

或类似的东西。 您可以在内部循环中设置一个variables,并在内部循环退出后立即在外部循环中检查该variables,如果合适的话中断。 我有点像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 

我很确定你可以在这里使用recursion来解决一些问题,但我不知道如果这对你来说是一个很好的select。

将你的循环逻辑放入一个产生循环variables并在完成时返回的迭代器 – 这里是一个简单的循环,它以行/列的forms显示图像,直到我们没有图像或不在图像的位置来放置图像:

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

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

为什么不保持循环,如果两个条件是真的? 我认为这是一个更pyathonic的方式:

 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 

不是吗?

祝一切顺利。

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

在Python while ... else结构中有一个隐藏的技巧,它可以用来模拟双中断,而不需要太多的代码修改/添加。 实质上,如果while条件为false,则会触发else块。 无论是例外, continue还是break触发else块。 有关更多信息,请参阅“ Python while语句上的Else子句 ”或while(v2.7)上的Python doc的答案。

 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条件(或添加一个标志variables)。 for循环的变化也存在,在循环完成之后else块被触发。

另一种减less迭代到单级循环的方法是通过使用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”,但不幸的是,这个答案是closures的

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

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

类似之前的一样,但更加紧凑。 (布尔人只是数字)

 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 # <--- ! 
 Try using an infinite generator. 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 
 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 
 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 

可能像下面的小技巧,如果不喜欢重构function

添加1个break_levelvariables来控制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 

您可以定义一个variables(例如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中的函数可以在代码中间创build,并且可以透明地访问周围的variables以供读取,并且可以使用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 

要突破多个嵌套循环,而不重构成一个函数,使用内置StopIterationexception的“模拟goto语句”:

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

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

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

尽pipe在多循环构造中不存在称为循环的标签,我们可以使用用户定义的例外来打破我们select的特定循环。 考虑下面的例子,让我们在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)被引发并捕获在同一个循环中而断开任何循环。 同样,每当十个地方有三个,我们用BreakLoop(3)分解成第三个循环。 每当百位有5时,我们用BreakLoop(2)分成第二个循环,而当千位有2个时,我们用BreakLoop(1)分解成第一个循环。

简而言之,在内部循环中引发exception(内置或用户定义的),并将其从您想要恢复控制的循环中捕获。 如果你想中断所有的循环,在所有的循环之外捕获exception。 (我没有在例子中显示这个例子)。

这将摆脱三个while循环

 while True: while True: while True: print("get me out") break break break