2009-03-17 38 views
175

Eventuali duplicati:
How to break out of multiple loops in Python?Scoppio di cicli nidificati

C'è un modo più semplice per uscire da cicli annidati di un'eccezione? (In Perl, si può dare etichette per ogni ciclo e almeno continuare un ciclo esterno.)

for x in range(10): 
    for y in range(10): 
     print x*y 
     if x*y > 50: 
      "break both loops" 

Vale a dire, c'è un modo più bello di:

class BreakIt(Exception): pass 

try: 
    for x in range(10): 
     for y in range(10): 
      print x*y 
      if x*y > 50: 
       raise BreakIt 
except BreakIt: 
    pass 

risposta

99

E 'stato almeno suggerita, ma anche rejected. Non credo ci sia un altro modo, a parte ripetere il test o riorganizzare il codice. A volte è un po 'fastidioso.

Nel rejection message, Mr van Rossum menziona utilizzando return, che è davvero ragionevole e qualcosa che ho bisogno di ricordare personalmente. :)

+9

I secondo utilizzando l'istruzione 'return'. Mi ha costretto a scrivere il ciclo interno in una seconda funzione, ma ho reso il codice molto più facile da capire. – vdboor

+0

Questa soluzione generica funziona anche quando il ciclo nested for è seguito da altre istruzioni. Per esempio. quando si esegue il ciclo su un elenco di frasi e si utilizzano diversi cicli per filtrare frasi specifiche in base all'esistenza di parole o numeri specifici, prima di eseguire il lavoro effettivo alla fine del ciclo esterno. – Anthon

+7

La soluzione di Mr van Rossum non funziona quando si è in un generatore, che è un buco abbastanza grande IMHO –

3

In questo caso particolare, è possibile unire i loop con un python moderno (3.0 e probabilmente anche 2.6) utilizzando itertools.product.

Io per me ho preso come regola generale, se annidi troppi cicli (come in più di 2), di solito sei in grado di estrarre uno dei loop in un metodo diverso o unire i loop in uno , come in questo caso.

8

È anche possibile refactoring il codice per utilizzare un generatore. Ma questa potrebbe non essere una soluzione per tutti i tipi di loop annidati.

476
for x in xrange(10): 
    for y in xrange(10): 
     print x*y 
     if x*y > 50: 
      break 
    else: 
     continue # executed if the loop ended normally (no break) 
    break # executed if 'continue' was skipped (break) 

Le stesse opere per cicli più profondi:

for x in xrange(10): 
    for y in xrange(10): 
     for z in xrange(10): 
      print x,y,z 
      if x*y*z == 30: 
       break 
     else: 
      continue 
     break 
    else: 
     continue 
    break 
+14

Per una spiegazione al riguardo: http://psung.blogspot.com.au/2007/12/for-else- in-python.html – aiham

+6

A causa dell'istruzione di richiesta obbligatoria per il ciclo esterno, questo in genere non funziona bene in situazioni in cui il ciclo nidificato non è l'unico codice nel ciclo for esterno. L'esempio OP potrebbe essere troppo semplice. – Anthon

+5

È possibile sostituire 'continue' con' ok = True' e 'break' con' if not ok: break'. –

52

Se siete in grado di estrarre il codice di ciclo in una funzione, una dichiarazione return può essere utilizzato per uscire dal ciclo più esterno, in qualsiasi momento.

def foo(): 
    for x in range(10): 
     for y in range(10): 
      print x*y 
      if x*y > 50: 
       return 
foo() 

Se è difficile da estrarre che la funzione è possibile utilizzare una funzione interna, come suggerisce @ bjd2385, per esempio

def your_outer_func(): 
    ... 
    def inner_func(): 
     for x in range(10): 
      for y in range(10): 
       print x*y 
       if x*y > 50: 
        return 
    inner_func() 
    ... 
+1

Potrebbe semplicemente usare una funzione nidificata in quel caso ... – bjd2385

+1

@ bjd2385 buon punto, post aggiornato con una funzione nidificata –

18

A volte uso una variabile booleana. Ingenuo, se vuoi, ma lo trovo abbastanza flessibile e comodo da leggere. Il test di una variabile può evitare di testare nuovamente condizioni complesse e può anche raccogliere risultati da diversi test nei loop interni.

x_loop_must_break = False 
    for x in range(10): 
     for y in range(10): 
      print x*y 
      if x*y > 50: 
       x_loop_must_break = True 
       break 
     if x_loop_must_break: break 
28

Utilizzare itertools.product!

from itertools import product 
for x, y in product(range(10), range(10)): 
    #do whatever you want 
    break 

Ecco un link al itertools.product nella documentazione pitone: http://docs.python.org/library/itertools.html#itertools.product

+1

Devi fare il rivestimento uno: >>> stampare "\ n" .join (map (str, takewhile (lambda i: i <= 50, (x * y per x, y nel prodotto (xrange (10), xrange (10)))))) –

+1

questo non affronta il problema principale della rottura di cicli annidati in piena generalità –

+1

Solo se stai cercando un modo per uscire da un loop alla volta, ma essere ancora in grado di uscire da entrambi. Per questo, è possibile utilizzare una funzione o un'eccezione. Trovo questo metodo più elegante quando non hai bisogno di uscire da uno dei loop alla volta. –

13

Se avete intenzione di sollevare un'eccezione, si potrebbe sollevare un StopIteration exception. Ciò renderà almeno l'intento ovvio.

+3

Questa è una buona idea. Non richiede la creazione di una nuova classe di eccezioni ed è molto facile da leggere. –