terminar - ¿Cómo romper con varios bucles en Python?
for i in range python español (28)
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()
¿Y por qué no seguir buceando si dos condiciones son ciertas? Creo que esta es una forma más pitónica:
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.
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
(Bystander-EDIT aquí, porque no hemos visto a @yak en mucho tiempo).
Información clave: solo parece que el bucle externo siempre se rompe. Pero si el bucle interno no se rompe, el bucle externo tampoco lo hará.
La declaración continue
es la magia aquí. Está en la cláusula for-else. Por definición eso sucede si no hay ruptura interior. En esa situación, elude perfectamente la ruptura exterior.
Dado que esta pregunta se ha convertido en una pregunta estándar para entrar en un bucle particular, me gustaría dar mi respuesta con un ejemplo utilizando 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 cuya 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 forma 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 con BreakLoop(2)
y cuando el lugar mil tiene 2, entramos en el primer bucle con 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).
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
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
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.
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 ...
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.
Introduzca una nueva variable que utilizará como un "interruptor de bucle". Primero asigne algo a él (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 el bucle salga, 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 anidados. Puedes salir de todo, o solo unos pocos. ¡Posibilidades infinitas! Espero que esto haya ayudado!
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 un gran control sobre cómo exactamente quiere que se rompa el programa, permitiéndole elegir cuándo quiere interrumpir y cuántos niveles bajar.
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 manera transparente para leer y con global
declaración global
o nonlocal
para escribir.
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
Mi primer instinto sería refactorizar el bucle anidado en una función y utilizar el return
para romper.
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.
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
Para salir de múltiples bucles anidados, 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 instrucciones goto para romper los bucles anidados.
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 en modo de caracteres. Para la opción "Q", queremos una salida inmediata. Eso es más excepcional.
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. Básicamente, esto evita el problema que resolvió y, por lo general, se puede aplicar en el caso más general (separación de múltiples 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 here ):
#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.
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
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 # <--- !
Tiendo a estar de acuerdo en que refactorizar en una función suele ser el mejor enfoque para este tipo de situación, pero cuando realmente necesita salir de los bucles anidados, 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 parezca 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.
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
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 de 2 a 20 veces más simple que la mayoría de las respuestas sugeridas.
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.
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 ejecutar comentando cada línea que contiene la break
una en una desde la parte inferior.
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 corresponde. Me gusta un poco el método GOTO, siempre que no te importe utilizar el módulo de broma de April Fool, no es Pythonic, pero tiene sentido.
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