Python: continua con l'iterazione successiva nel ciclo esterno


135

Volevo sapere se ci sono modi incorporati per continuare alla prossima iterazione nel ciclo esterno in Python. Ad esempio, considera il codice:

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

Voglio che questa istruzione continue esca dal ciclo jj e vada all'elemento successivo nel ciclo ii. Posso implementare questa logica in qualche altro modo (impostando una variabile flag), ma c'è un modo semplice per farlo, o è come chiedere troppo?


11
Esiste effettivamente un'istruzione goto funzionante per Python: entrian.com/goto . È stato rilasciato come uno scherzo del pesce d'aprile :-), ma dovrebbe funzionare.
codeape

3
Oh, per favore, non usare quella barzelletta! È straordinariamente intelligente, ma in seguito sarai triste se lo inserirai nel tuo codice.
Ned Batchelder,

Risposte:


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

In un caso generale, quando hai più livelli di loop e breaknon funziona per te (perché vuoi continuare uno dei loop superiori, non quello proprio sopra quello corrente), puoi fare una delle seguenti

Rifattorizza i loop da cui vuoi fuggire in una funzione

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


for i in ...:
    inner()

Lo svantaggio è che potrebbe essere necessario passare a quella nuova funzione alcune variabili, che erano in precedenza nell'ambito. Puoi semplicemente passarli come parametri, renderli variabili di istanza su un oggetto (creare un nuovo oggetto solo per questa funzione, se ha senso), oppure variabili globali, singoli punti, qualunque cosa (ehm, ehm).

Oppure puoi definire inneruna funzione nidificata e lasciare che catturi solo ciò di cui ha bisogno (potrebbe essere più lento?)

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

Usa le eccezioni

Filosoficamente, ecco a cosa servono le eccezioni, interrompendo il flusso del programma attraverso i blocchi di programmazione strutturati (se, per, mentre) quando necessario.

Il vantaggio è che non è necessario suddividere il singolo pezzo di codice in più parti. Questo è utile se è un tipo di calcolo che stai progettando mentre lo scrivi in ​​Python. L'introduzione di astrazioni a questo punto iniziale può rallentare.

La cosa negativa di questo approccio è che gli autori di interpreti / compilatori di solito assumono che le eccezioni siano eccezionali e ottimizzino di conseguenza per loro.

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

Crea una speciale classe di eccezione per questo, in modo da non rischiare di mettere a tacere accidentalmente un'altra eccezione.

Qualcos'altro del tutto

Sono sicuro che ci sono ancora altre soluzioni.


Non riesco a credere di non aver pensato di spostare il mio secondo ciclo in un altro metodo. Sto diventando lento
pmccallum,

1
Per me, usare Exceptions è un buon modo per raggiungere questo obiettivo. Concordo con @ user7610 - "filosoficamente, ecco a cosa servono le eccezioni".
Renato Byrro,

Buona vecchia variabile booleana e istruzioni If?
MrR,

L'OP è alla ricerca di soluzioni alternative a questo, "Posso implementare questa logica in qualche altro modo (impostando una variabile flag) [...]"
user7610

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

Break interromperà il ciclo interno e il blocco1 non verrà eseguito (verrà eseguito solo se il ciclo interno viene chiuso normalmente).


1
Ciao, ci sono altre opzioni come questa? Perché voglio fare un altro per il ciclo nel blocco1, e così il mio codice andrebbe a 3 livelli di profondità. Strana situazione.
Sahas,

3
Per me sembra che tu stia provando a fare qualcosa per i loop che dovrebbero essere affrontati meglio in un modo diverso ...
Kimvais,

Sì. Ecco perché non ho usato la struttura for..else. Ora avrei ancora bisogno di loop, ma userò le variabili flag per deviare il controllo.
Sahas,

3
for...elseè spesso un costrutto utile, sebbene possa essere fonte di confusione. Basta ricordare che elsesignifica "nessuna pausa" in questo contesto.
asmeurer

Questo sembra essere limitato a soli due strati di anelli. Ho bisogno di aggiornare un po 'di codice che ha tre cicli nidificati e un nuovo requisito del cliente significa che in determinate circostanze il ciclo più interno deve continuare la successiva iterazione del ciclo più esterno. Presumo che il tuo suggerimento non si applicherebbe a quello scenario.
Kasperd,

42

In altre lingue è possibile etichettare il loop e interrompere dal loop etichettato. La proposta di potenziamento di Python (PEP) 3136 ha suggerito di aggiungerli a Python ma Guido l'ha respinto :

Tuttavia, lo sto rifiutando sulla base del fatto che un codice così complicato da richiedere questa funzione è molto raro. Nella maggior parte dei casi esistono soluzioni alternative che producono codice pulito, ad esempio utilizzando 'return'. Mentre sono sicuro che ci sono alcuni casi (rari) reali in cui la chiarezza del codice risentirebbe di un refactoring che rende possibile l'uso del ritorno, questo è compensato da due problemi:

  1. La complessità aggiunta alla lingua, in modo permanente. Ciò influisce non solo su tutte le implementazioni di Python, ma anche su tutti gli strumenti di analisi dei sorgenti, oltre ovviamente a tutta la documentazione per il linguaggio.

  2. La mia aspettativa che la funzione venga abusata più di quanto verrà utilizzata correttamente, portando a una netta diminuzione della chiarezza del codice (misurata su tutto il codice Python scritto d'ora in poi). I programmatori pigri sono ovunque e prima che tu lo sappia hai un incredibile casino tra le mani di codice incomprensibile.

Quindi, se è quello che speravi, sei sfortunato, ma guarda una delle altre risposte in quanto ci sono buone opzioni lì.


4
Interessante. Sono d'accordo con Guido qui. Mentre sarebbe bello per alcuni casi sarebbe abusato. Un altro motivo per non implementarlo è che in questo momento è abbastanza semplice portare avanti e indietro il codice tra C e Python. Una volta che Python inizia a raccogliere funzionalità che mancano in altre lingue, questo diventa più difficile. Prendi ad esempio il fatto che puoi avere un'istruzione else su un ciclo for in Python ... questo rende il codice meno portabile in altre lingue.
eric.frederich,

2
Tutti
chiamano

4
Questo è più un erudizione che una buona contro-argomentazione, ma mi sembra che il comportamento di for-elsesia più complicato, più difficile da leggere e probabilmente più abusato (se non addirittura un errore assoluto) di quanto sarebbero i loop con nome. Penso che avrei usato una parola chiave diversa da else- forse qualcosa di simile resumesarebbe stato buono? Sei breaknel ciclo e il resumeè subito dopo?
ArtOfWarfare il

5
Questo mi rende triste. Non riesco a credere quanto amo e odio Python allo stesso tempo. Così bello, eppure così wtf.
jlh

5
@jlh Principalmente wtf per me. A volte penso che voglia essere diverso non per uno scopo legittimo, ma solo per essere diverso. Questo è un buon esempio di ciò. Mi viene spesso in mente la necessità di rompere gli anelli esterni.
Rikaelus,

14

Penso che potresti fare qualcosa del genere:

for ii in range(200):
    restart = False
    for jj in range(200, 400):
        ...block0...
        if something:
            restart = True
            break
    if restart:
        continue
    ...block1...

4
-1: L'OP ha affermato chiaramente che sapeva di poter fare qualcosa del genere, e questo sembra solo una versione più disordinata della risposta accettata (che precede la tua di 8 mesi, quindi non poteva essere che ti sei perso risposta).
ArtOfWarfare il

10
La risposta accettata non è più chiaro se non hai mai visto for, elseprima di (e credo che anche la maggior parte persone che hanno non ricordo fuori dalla parte superiore della testa come funziona).
asmeurer,

3

Penso che uno dei modi più semplici per raggiungere questo obiettivo sia quello di sostituire "continua" con la frase "break", cioè

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

Ad esempio, ecco il codice semplice per vedere come va esattamente:

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

2

Un altro modo per affrontare questo tipo di problema è utilizzare Exception ().

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

Per esempio:

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

risultato:

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

Supponendo di voler saltare al ciclo n esterno dal ciclo m se 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

risultato:

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

Link di riferimento: http://www.programming-idioms.org/idiom/42/continue-outer-loop/1264/python


1

Vogliamo trovare qualcosa e quindi interrompere l'iterazione interiore. Uso un sistema di bandiera.

for l in f:
    flag = True
    for e in r:
        if flag==False:continue
        if somecondition:
            do_something()
            flag=False

Non so perché la tua soluzione sia stata sottoposta a downgrade; qualcuno ha pubblicato praticamente esattamente la stessa cosa ed è stato votato per 10 volte
Locane il

Non sono molto fortunato con StackOverflow.
Ester

1
Forse perché c'è praticamente esattamente la stessa cosa già pubblicata qui, credo ... E il fatto False:continueè ... formattazione insolita. Come spesso accade nei sistemi "naturali" in cui l'esponenziale è la norma, devi solo essere fortunato alcune volte su SO per accumulare una quantità significativa di punti reputazione. Comunque, le mie risposte "migliori" sono di solito le meno popolari.
user7610

0

Ho appena fatto qualcosa del genere. La mia soluzione per questo era sostituire l'interno per il ciclo con una comprensione dell'elenco.

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

dove op è un operatore booleano che agisce su una combinazione di ii e jj. Nel mio caso, se una delle operazioni è risultata vera, avevo finito.

Questo non è poi così diverso dal suddividere il codice in una funzione, ma ho pensato che usare l'operatore "any" per fare un OR logico su un elenco di booleani e fare la logica tutto in una riga fosse interessante. Evita anche la chiamata di funzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.