¿Cómo romper con múltiples bucles en Python?

Dado el siguiente código (que no funciona):

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 

¿Hay alguna manera de hacer que esto funcione? ¿O tengo que hacer una comprobación para salir del bucle de entrada, y luego otra, más limitada, verificar el bucle exterior para romper todos juntos si el usuario está satisfecho?

Edit-FYI: get_input es una función corta que escribí que admite la visualización de valores predeterminados y de solicitud y toda esa fantasía y devuelve stdin.readline().strip()

Mi primer instinto sería refactorizar el bucle nested en una función y utilizar el return para romper.

Aquí hay otro enfoque que es corto. La desventaja es que solo puedes romper el bucle externo, pero a veces es exactamente lo que quieres.

 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 

Esto usa el constructo for / else explicado en: ¿Por qué python usa ‘else’ después de los bucles for y while?

Información clave: solo parece como si el bucle externo siempre se rompe. Pero si el bucle interno no se rompe, el bucle externo tampoco lo hará.

La statement continue es la magia aquí. Está en la cláusula for-else. Por definición eso pasa si no hay ruptura interior. En esa situación, eludir perfectamente la ruptura exterior.

PEP 3136 propone rotular rotura / continuar. Guido lo rechazó porque “el código tan complicado para requerir esta característica es muy raro”. Sin embargo, el PEP menciona algunas soluciones alternativas (como la técnica de excepción), mientras que Guido siente que refactorizar el uso del retorno será más sencillo en la mayoría de los casos.

Primero, la lógica ordinaria es útil.

Si, por alguna razón, las condiciones de terminación no pueden ser resueltas, las excepciones son un plan alternativo.

 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 

Para este ejemplo específico, una excepción puede no ser necesaria.

Por otro lado, a menudo tenemos las opciones “Y”, “N” y “Q” en las aplicaciones de modo de caracteres. Para la opción “Q”, queremos una salida inmediata. Eso es más excepcional.

Tiendo a estar de acuerdo en que la refactorización en una función suele ser el mejor enfoque para este tipo de situación, pero cuando realmente necesita salir de los bucles nesteds, aquí hay una interesante variante del enfoque de aumento de excepciones que describió @ S.Lott. Utiliza Python’s with sentencias para hacer que el aumento de excepciones se vea un poco mejor. Defina un nuevo administrador de contexto (solo tiene que hacer esto una vez) con:

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

Ahora puedes usar este administrador de contexto de la siguiente manera:

 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" 

Ventajas: (1) es un poco más limpio (no hay un bloque explícito de prueba de excepción), y (2) obtiene una subclase de Exception para cada uso de nested_break ; no es necesario declarar su propia subclase de Exception cada vez.

Primero, también puede considerar hacer que el proceso de obtención y validación de una entrada sea una función; dentro de esa función, solo puede devolver el valor si es correcto, y continuar girando en el bucle while si no lo está. Básicamente, esto evita el problema que resolvió y, por lo general, se puede aplicar en el caso más general (romper varios bucles). Si absolutamente debe mantener esta estructura en su código, y realmente no quiere tratar con los booleanos contables …

También puede usar goto de la siguiente manera (usando un módulo de April Fools desde aquí ):

 #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 

Lo sé, lo sé, “no usarás goto” y todo eso, pero funciona bien en casos extraños como este.

Introduzca una nueva variable que utilizará como un “interruptor de bucle”. Primero, asigne algo (Falso, 0, etc.), y luego, dentro del bucle externo, antes de romper con él, cambie el valor a otra cosa (Verdadero, 1, …). Una vez que sale el bucle, haga que el bucle ‘padre’ verifique ese valor Déjame demostrar:

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

Si tienes un bucle infinito, esta es la única salida; para otros loops la ejecución es mucho más rápida. Esto también funciona si tienes muchos bucles nesteds. Puedes salir de todo, o solo unos pocos. ¡Posibilidades infinitas! Espero que esto haya ayudado!

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

o algo así. Podría establecer una variable en el bucle interno, y verificarla en el bucle externo inmediatamente después de que el bucle interno salga, rompiendo si es apropiado. Me gusta un poco el método GOTO, siempre que no te importe usar el módulo de broma de April Fool, no es Pythonic, pero tiene sentido.

Esta no es la forma más bonita de hacerlo, pero en mi opinión, es la mejor.

 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 

Estoy bastante seguro de que también podrías resolver algo utilizando la recursión, pero no sé si esa es una buena opción para ti.

Para salir de varios bucles nesteds, sin refactorizar en una función, use una “instrucción de simulación de goto” con la excepción StopIteration incorporada :

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

Vea esta discusión sobre el uso de declaraciones goto para romper los bucles nesteds.

Factoriza tu lógica de bucle en un iterador que produce las variables de bucle y regresa cuando está listo. Aquí hay un sencillo que presenta las imágenes en filas / columnas hasta que nos quedamos sin imágenes o lugares para colocarlas:

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

Esto tiene la ventaja de dividir la complicada lógica de bucle y el procesamiento …

¿Y por qué no seguir buceando si dos condiciones son ciertas? Creo que esta es una forma más pythonica:

 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 

¿No es así?

Todo lo mejor.

En este caso, como han señalado otros también, la descomposición funcional es el camino a seguir. Código en 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 

Hay un truco oculto en la estructura de Python while ... else , se puede usar para simular el doble salto sin muchos cambios / adiciones de código. En esencia, si la condición while es falsa, se activa el bloque else . Ninguna de las excepciones, continue o break activan el bloque else . Para obtener más información, consulte las respuestas a la ” cláusula Else en Python while statement “, o el documento en Python 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 

El único inconveniente es que necesita mover la condición de doble ruptura a la condición while (o agregar una variable de marca). También existen variaciones de esto para el bucle for , donde el bloque else se activa después de completar el bucle.

Otra forma de reducir su iteración a un bucle de un solo nivel sería mediante el uso de generadores como también se especifica en la referencia de python

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

Podrías escalarlo a cualquier número de niveles para el bucle

El inconveniente es que ya no se puede romper un solo nivel. Es todo o nada.

Otro inconveniente es que no funciona con un bucle while. Originalmente quería publicar esta respuesta en Python – `break ‘out of all loops pero desafortunadamente eso está cerrado como un duplicado de este

Mi razón para venir aquí es que tenía un bucle externo y uno interno así:

 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 

Como puede ver, en realidad no irá a la siguiente x, sino que irá a la siguiente y en su lugar.

Lo que encontré para resolver esto simplemente fue correr a través de la matriz dos veces:

 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 

Sé que este fue un caso específico de la pregunta de OP, pero lo estoy publicando con la esperanza de que ayude a alguien a pensar sobre su problema de manera diferente mientras mantengo las cosas simples.

Mediante el uso de una función:

 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() 

Intente ejecutar los códigos anteriores comentando también la return .

Sin utilizar ninguna función:

 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 

Ahora, ejecute los códigos anteriores tal como están primero y luego intente correr comentando cada línea que contiene la break una en una desde la parte inferior.

 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 

Probablemente un pequeño truco como el de abajo lo hará si no prefiere refactorial en función

agregada 1 variable break_level para controlar la condición del bucle 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 

Puede definir una variable (por ejemplo, break_statement ), luego cambiarla a un valor diferente cuando ocurra una condición de dos interrupciones y usarla en la instrucción if para separarse también del segundo bucle.

 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 

Trate de usar un generador infinito.

 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 

Me gustaría recordarle que las funciones en Python pueden crearse justo en el medio del código y pueden acceder a las variables circundantes de forma transparente para la lectura y con global statement global o nonlocal para la escritura.

Por lo tanto, puede usar una función como una “estructura de control rompible”, definiendo un lugar al que desea regresar:

 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 

Esperemos que esto ayude:

 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 

Una forma fácil de convertir varios bucles en un solo bucle que se puede numpy.ndindex es usar 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! 

Tiene que indexar sus objetos, en lugar de poder iterar a través de los valores explícitamente, pero al menos en casos simples parece ser aproximadamente 2 a 20 veces más simple que la mayoría de las respuestas sugeridas.

Soluciones en 2 maneras

Con un ejemplo: ¿Son estas dos matrices iguales / iguales?
matrix1 y matrix2 son matrices del mismo tamaño, n, 2 dimensiones.

Primera solución , sin función.

 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 

Segunda solución , con una función.
Esta es la solución definitiva para mi caso.

 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 

¡Que tengas un buen día!

Similar a la anterior, pero más compacta. (Los booleanos son solo números)

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

Dado que esta pregunta se ha convertido en una pregunta estándar para entrar en un bucle en particular, me gustaría dar mi respuesta con un ejemplo usando Exception .

Si bien no existe una etiqueta con el nombre de ruptura de bucle en una construcción de bucle múltiple, podemos hacer uso de Excepciones definidas por el usuario para dividir en un bucle particular de nuestra elección. Considere el siguiente ejemplo donde imprimimos todos los números de hasta 4 dígitos en el sistema de numeración base-6:

 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 

Cuando imprimimos la salida, nunca obtendremos ningún valor cuyo lugar de unidad sea con 4. En ese caso, no salimos de ningún bucle ya que BreakLoop(4) se levanta y se captura en el mismo bucle. De manera similar, cuando diez posiciones tienen 3, entramos en el tercer bucle utilizando BreakLoop(3) . Cada vez que cien lugares tienen 5, entramos en el segundo bucle utilizando BreakLoop(2) y cuando el lugar mil tiene 2, entramos en el primer bucle utilizando BreakLoop(1) .

En resumen, suba su Excepción (incorporada o definida por el usuario) en los bucles internos y cójala en el bucle desde donde desea reanudar su control. Si desea interrumpir todos los bucles, capture la Excepción fuera de todos los bucles. (No he mostrado este caso en el ejemplo).

La forma en que resuelvo esto es mediante la definición de una variable a la que se hace referencia para determinar si pasas al siguiente nivel o no. En este ejemplo, esta variable se llama ‘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!''') 

Esto le da mucho control sobre cómo exactamente quiere que se rompa el progtwig, lo que le permite elegir cuándo quiere romper y cuántos niveles bajan.