Python: continuando con la próxima iteración en el bucle externo

135

Quería saber si hay formas integradas de continuar con la próxima iteración en el bucle externo en Python. Por ejemplo, considere el código:

for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            continue
    ...block1...

Quiero que esta declaración de continuación salga del bucle jj y pase al siguiente elemento en el bucle ii. Puedo implementar esta lógica de alguna otra manera (estableciendo una variable de marca), pero ¿hay una manera fácil de hacer esto, o es como pedir demasiado?

Sahas
fuente
11
En realidad, existe una declaración de trabajo para ir a Python: entrian.com/goto . Fue lanzado como una broma de los inocentes :-), pero se supone que funciona.
codeape
3
¡Oh, por favor, no uses esa broma! Es notablemente inteligente, pero te entristecerás más tarde si lo pones en tu código.
Ned Batchelder

Respuestas:

71
for i in ...:
    for j in ...:
        for k in ...:
            if something:
                # continue loop i

En un caso general, cuando tiene múltiples niveles de bucle y breakno funciona para usted (porque desea continuar uno de los bucles superiores, no el que está justo encima del actual), puede hacer uno de los siguientes

Refactorice los bucles de los que desea escapar a una función

def inner():
    for j in ...:
        for k in ...:
            if something:
                return


for i in ...:
    inner()

La desventaja es que es posible que deba pasar a esa nueva función algunas variables, que anteriormente estaban en el alcance. Puede pasarlos como parámetros, convertirlos en variables de instancia en un objeto (crear un nuevo objeto solo para esta función, si tiene sentido) o variables globales, singletons, lo que sea (ehm, ehm).

O puede definir innercomo una función anidada y dejar que solo capture lo que necesita (¿puede ser más lento?)

for i in ...:
    def inner():
        for j in ...:
            for k in ...:
                if something:
                    return
    inner()

Use excepciones

Filosóficamente, esto es para lo que se hacen excepciones, rompiendo el flujo del programa a través de los bloques de construcción de la programación estructurada (si es, por, mientras) cuando sea necesario.

La ventaja es que no tiene que dividir el código único en varias partes. Esto es bueno si es algún tipo de cálculo que está diseñando mientras lo escribe en Python. Introducir abstracciones en este punto temprano puede ralentizarlo.

Lo malo de este enfoque es que los autores de intérpretes / compiladores generalmente asumen que las excepciones son excepcionales y las optimizan en consecuencia.

class ContinueI(Exception):
    pass


continue_i = ContinueI()

for i in ...:
    try:
        for j in ...:
            for k in ...:
                if something:
                    raise continue_i
    except ContinueI:
        continue

Cree una clase de excepción especial para esto, de modo que no corra el riesgo de silenciar accidentalmente alguna otra excepción.

Algo completamente diferente

Estoy seguro de que todavía hay otras soluciones.

user7610
fuente
No puedo creer que no haya pensado en mover mi segundo ciclo a otro método. Me estoy poniendo lento
pmccallum
1
Para mí, usar Excepciones es una buena manera de lograr esto. Estoy de acuerdo con @ user7610 - "filosóficamente, para eso son las excepciones".
Renato Byrro
¿Buena vieja variable booleana y declaraciones If?
MrR
El OP está buscando soluciones alternativas a eso, "Puedo implementar esta lógica de alguna otra manera (estableciendo una variable de
marca
149
for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            break
    else:
        ...block1...

Break romperá el bucle interno y block1 no se ejecutará (solo se ejecutará si el bucle interno sale normalmente).

culebrón
fuente
1
Hola, ¿hay otras opciones como esta? Porque quiero hacer otro bucle for en el bloque 1, y así mi código iría a 3 niveles de profundidad. Situación rara
Sahas
3
Para mí, esto suena como si estuvieras tratando de hacer algo con bucles que se abordarían de una manera diferente ...
Kimvais
Si. Por eso no utilicé la estructura for..else. Ahora todavía necesitaría bucles, pero usaré variables de bandera para desviar el control.
Sahas
3
for...elseA menudo es una construcción útil, aunque puede ser confusa. Solo recuerde que elsesignifica "sin descanso" en este contexto.
asmeurer
Esto parece estar limitado a solo dos capas de bucles. Necesito actualizar un código que tiene tres bucles anidados, y un nuevo requisito del cliente significa que, en ciertas circunstancias, el bucle más interno debe continuar la próxima iteración del bucle más externo. Supongo que su sugerencia no se aplicaría a ese escenario.
kasperd
42

En otros idiomas, puede etiquetar el bucle y separarse del bucle etiquetado. La Propuesta de Mejora de Python (PEP) 3136 sugirió agregar estos a Python pero Guido lo rechazó :

Sin embargo, lo estoy rechazando porque el código tan complicado para requerir esta característica es muy raro. En la mayoría de los casos, existen soluciones alternativas que producen código limpio, por ejemplo, usando 'return'. Si bien estoy seguro de que hay algunos casos reales (raros) en los que la claridad del código sufriría una refactorización que hace posible usar return, esto se compensa con dos problemas:

  1. La complejidad añadida al lenguaje, de forma permanente. Esto afecta no solo a todas las implementaciones de Python, sino también a todas las herramientas de análisis de código fuente, además, por supuesto, a toda la documentación del lenguaje.

  2. Espero que se abusará más de la característica de lo que se usará correctamente, lo que lleva a una disminución neta en la claridad del código (medido en todo el código Python escrito a partir de ahora). Los programadores perezosos están en todas partes, y antes de que te des cuenta tienes un desorden increíble en tus manos de código ininteligible.

Entonces, si eso es lo que esperabas, no tienes suerte, pero mira una de las otras respuestas, ya que hay buenas opciones allí.

Dave Webb
fuente
44
Interesante. Estoy de acuerdo con Guido aquí. Si bien sería bueno para algunos casos, sería abusado. Otra razón para no implementarlo es que en este momento es bastante sencillo transferir el código entre C y Python. Una vez que Python comienza a elegir características que carecen de otros idiomas, esto se vuelve más difícil. Tomemos, por ejemplo, el hecho de que puede tener una instrucción else en un bucle for en Python ... esto hace que el código sea menos portátil a otros idiomas.
eric.frederich
2
Todos
saludan a
44
Esto es más un descuido que un buen contraargumento, pero me parece que el comportamiento de for-elsees más complicado, más difícil de leer y probablemente más abusado (si no es un error absoluto) de lo que serían los bucles con nombre. Creo que hubiera usado una palabra clave diferente que else, ¿tal vez algo así resumehubiera sido bueno? ¿Estás breaken el circuito y resumeestá justo después?
ArtOfWarfare
55
Esto me pone triste. No puedo creer cómo amo y odio Python al mismo tiempo. Tan hermoso, pero tan wtf.
jlh
55
@jlh Principalmente wtf para mí. A veces pienso que quiere ser diferente, no para un propósito legítimo, sino solo para ser diferente. Este es un buen ejemplo de eso. Me encuentro con la necesidad de romper los bucles externos con bastante frecuencia.
Rikaelus
14

Creo que podrías hacer algo como esto:

for ii in range(200):
    restart = False
    for jj in range(200, 400):
        ...block0...
        if something:
            restart = True
            break
    if restart:
        continue
    ...block1...
asmeurer
fuente
44
-1: El OP declaró claramente que sabían que podían hacer algo como esto, y esto solo parece una versión más desordenada de la respuesta aceptada (que es anterior a la suya en 8 meses, por lo que no podría haber sido que simplemente se perdió la respuesta aceptada responder).
ArtOfWarfare
10
La respuesta aceptada es no más claro si usted nunca ha visto for, elseantes (y creo que incluso la mayoría de las personas que tienen no pueden recordar de la parte superior de su cabeza cómo funciona).
asmeurer
3

Creo que una de las formas más fáciles de lograr esto es reemplazar la declaración "continuar" con "romper", es decir

for ii in range(200):
 for jj in range(200, 400):
    ...block0...
    if something:
        break
 ...block1...       

Por ejemplo, aquí está el código fácil para ver cómo funciona exactamente:

for i in range(10):
    print("doing outer loop")
    print("i=",i)
    for p in range(10):
        print("doing inner loop")
        print("p=",p)
        if p==3:
            print("breaking from inner loop")
            break
    print("doing some code in outer loop")
Khelina Fedorchuk
fuente
2

Otra forma de lidiar con este tipo de problema es usar Exception ().

for ii in range(200):
    try:
        for jj in range(200, 400):
            ...block0...
            if something:
                raise Exception()
    except Exception:
        continue
    ...block1...

Por ejemplo:

for n in range(1,4):
    for m in range(1,4):
        print n,'-',m

resultado:

    1-1
    1-2
    1-3
    2-1
    2-2
    2-3
    3-1
    3-2
    3-3

Suponiendo que queremos saltar al bucle n externo desde el bucle m si m = 3:

for n in range(1,4):
    try:
        for m in range(1,4):
            if m == 3:
                raise Exception()            
            print n,'-',m
    except Exception:
        continue

resultado:

    1-1
    1-2
    2-1
    2-2
    3-1
    3-2

Enlace de referencia: http://www.programming-idioms.org/idiom/42/continue-outer-loop/1264/python

Patricio
fuente
1

Queremos encontrar algo y luego detener la iteración interna. Yo uso un sistema de bandera.

for l in f:
    flag = True
    for e in r:
        if flag==False:continue
        if somecondition:
            do_something()
            flag=False
Esther
fuente
No sé por qué su solución fue rechazada; alguien publicó básicamente exactamente lo mismo y recibió 10 votos a favor
Locane
No tengo mucha suerte con stackoverflow.
Esther
1
Quizás porque básicamente hay exactamente lo mismo publicado aquí, supongo ... Y la False:continuecosa es ... un formato inusual. Como suele ser el caso en los sistemas "naturales" donde la norma es exponencial, solo tienes que tener suerte unas pocas veces en SO para acumular una cantidad significativa de puntos de reputación. De todos modos, mis "mejores" respuestas suelen ser las menos populares.
user7610
0

Acabo de hacer algo como esto. Mi solución para esto fue reemplazar el interior por el bucle con una lista de comprensión.

for ii in range(200):
    done = any([op(ii, jj) for jj in range(200, 400)])
    ...block0...
    if done:
        continue
    ...block1...

donde op es un operador booleano que actúa sobre una combinación de ii y jj. En mi caso, si alguna de las operaciones devuelve verdadero, ya estaba hecho.

Esto realmente no es tan diferente de dividir el código en una función, pero pensé que usar el operador "any" para hacer un OR lógico en una lista de booleanos y hacer la lógica todo en una línea era interesante. También evita la llamada a la función.

cagem12
fuente