首页 文章

如何在Python中打破多个循环?

提问于
浏览
346

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

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

有没有办法让这项工作?或者我是否进行了一次检查以突破输入循环,然后另一个更有限的检查外部循环以在用户满意时将所有内容分开?

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

28 回答

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

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

  • 11

    PEP 3136建议标记为break / continue . Guido rejected it因为"code so complicated to require this feature is very rare" . PEP确实提到了一些变通方法(例如异常技术),而Guido认为在大多数情况下使用return的重构会更简单 .

  • 97

    将多个循环转换为单个可破坏循环的简单方法是使用 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倍 .

  • 2

    我的第一直觉是将嵌套循环重构为函数并使用 return 来突破 .

  • 135

    类似于前一个,但更紧凑 . (布尔人只是数字)

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

    如果不喜欢将refactorial转化为函数,那么下面可能会有一些小技巧

    添加了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
    
  • 2

    By using a function:

    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 来运行上述代码 .

    Without using any function:

    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 的每一行来运行 .

  • 0

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

    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上发布这个答案,但遗憾的是,这个答案已经关闭了

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

  • -2
    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
    
  • 7

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

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

    如果由于某种原因无法解决终止条件,则例外是后备计划 .

    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”选项,我们希望立即退出 . 那更加特别 .

  • 3

    尝试使用无限发电机 .

    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
    
  • 35
    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
    
  • 386

    Python while ... else 结构中有一个隐藏的技巧,可以用来模拟双重中断而无需更改代码 . 实质上,如果 while 条件为假,则触发 else 块 . 两个例外, continuebreak 都不会触发 else 块 . 有关更多信息,请参阅“Else clause on Python while statement”或Python doc 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 块在循环完成后被触发 .

  • 46

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

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

  • 0

    由于这个问题已成为打入特定循环的标准问题,我想用 Exception 给出我的答案 .

    虽然在多重循环结构中不存在名为break of loop的标签,但我们可以利用User-defined Exceptions进入我们选择的特定循环 . 考虑以下示例,让我们在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
    

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

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

  • 8

    我倾向于同意重构函数通常是这种情况的最佳方法,但是当你真的需要打破嵌套循环时,这里's an interesting variant of the exception-raising approach that @S.Lott described. It uses Python' s 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-except块),(2)每次使用 nested_break 时都会得到一个定制的 Exception 子类;每次都不需要声明自己的 Exception 子类 .

  • 0

    我解决这个问题的方法是定义一个被引用的变量来确定你是否突破到了下一个级别 . 在此示例中,此变量称为“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!''')
    

    这样可以很好地控制程序打破程度的准确程度,允许您选择何时打破以及打破多少级别 .

  • 1

    如果两个条件成立,为什么不保持循环?我认为这是一种更加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
    

    不是吗?

    祝一切顺利 .

  • 13

    引入一个新变量,将其用作“循环断路器” . 首先为它指定一些东西(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   # <--- !
    

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

  • 9

    要打破多个嵌套循环而不重构函数,请使用内置StopIteration exception的"simulated goto statement":

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

    有关使用goto语句打破嵌套循环的信息,请参阅this discussion .

  • 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
    
  • 0

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

    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
    

    (Bystander-EDIT在这里,因为我们在looong时间没见过@yak . )

    关键见解:似乎外环总是断裂 . But if the inner loop doesn't break, the outer loop won't either.

    continue 语句在这里很神奇 . 它在for-else子句中 . By definition如果没有内心突破就会发生这种情况 . 在那种情况下, continue 整齐地规避了外围的突破 .

  • 24

    这不是最漂亮的方式,但在我看来,这是最好的方式 .

    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
    

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

  • 1

    我想提醒您,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
    
  • 1

    希望这会有所帮助:

    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
    
  • -2

    将你的循环逻辑分解为一个迭代器,它产生循环变量并在完成后返回 - 这是一个简单的映射行/列的图像,直到我们没有图像或没有放置它们的地方:

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

    这有利于拆分复杂的循环逻辑和处理......

相关问题