Skip to content

Uscire dai loop annidati in Python

Python

Questo articolo descrive come interrompere i cicli nidificati in Python.

  • Vieni a scrivere loop annidati in Python
  • Rompi i loop nidificati con else e continua
  • Rompi i cicli annidati con una variabile flag
  • Evita i loop nidificati con itertools.product()
  • Confronto di velocità

Vedere l’articolo seguente per l’utilizzo di base del ciclo per in Python.

Vieni a scrivere loop annidati in Python

In Python, i loop nidificati (loop multipli) sono scritti come segue. I blocchi sono rappresentati da rientri in Python, quindi aggiungi semplicemente più rientri.

l1 = [1, 2, 3]
l2 = [10, 20, 30]

for i in l1:
    for j in l2:
        print(i, j)
# 1 10
# 1 20
# 1 30
# 2 10
# 2 20
# 2 30
# 3 10
# 3 20
# 3 30

Quando l’interruzione viene eseguita nel ciclo interno, esce solo dal ciclo interno e il ciclo esterno continua.

for i in l1:
    for j in l2:
        print(i, j)
        if i == 2 and j == 20 :
            print('BREAK')
            break
# 1 10
# 1 20
# 1 30
# 2 10
# 2 20
# BREAK
# 3 10
# 3 20
# 3 30

Rompi i loop nidificati con else e continua

Nel ciclo for di Python, puoi usare else e continuare oltre a break.

Puoi interrompere tutti i loop con else e continuare.

for i in l1:
    for j in l2:
        print(i, j)
        if i == 2 and j == 20:
            print('BREAK')
            break
    else:
        continue
    break
# 1 10
# 1 20
# 1 30
# 2 10
# 2 20
# BREAK

Il codice con la spiegazione è il seguente.

for i in l1:
    print('Start outer loop')

    for j in l2:
        print('--', i, j)
        if i == 2 and j == 20:
            print('-- BREAK inner loop')
            break
    else:
        print('-- Finish inner loop without BREAK')
        continue

    print('BREAK outer loop')
    break
# Start outer loop
# -- 1 10
# -- 1 20
# -- 1 30
# -- Finish inner loop without BREAK
# Start outer loop
# -- 2 10
# -- 2 20
# -- BREAK inner loop
# BREAK outer loop

Quando il ciclo interno termina normalmente senza interruzioni, viene eseguita la clausola else. Questa continua è per il ciclo esterno e salta l’interruzione nel ciclo esterno e continua al ciclo successivo.

Quando il ciclo interno termina con un’interruzione, continua nella clausola else non viene eseguita. In questo caso, viene eseguita l’interruzione nel ciclo esterno.

Di conseguenza, ogni volta che il ciclo interno termina con un’interruzione, viene eseguita anche l’interruzione nel ciclo esterno.

L’idea è la stessa anche se il numero di loop aumenta. Un esempio di un ciclo triplo è il seguente.

l1 = [1, 2, 3]
l2 = [10, 20, 30]
l3 = [100, 200, 300]

for i in l1:
    for j in l2:
        for k in l3:
            print(i, j, k)
            if i == 2 and j == 20 and k == 200:
                print('BREAK')
                break
        else:
            continue
        break
    else:
        continue
    break
# 1 10 100
# 1 10 200
# 1 10 300
# 1 20 100
# 1 20 200
# 1 20 300
# 1 30 100
# 1 30 200
# 1 30 300
# 2 10 100
# 2 10 200
# 2 10 300
# 2 20 100
# 2 20 200
# BREAK

Rompi i cicli annidati con una variabile flag

Il modo sopra di usare altrimenti e continuare può essere difficile da capire per chi non ha familiarità con Python.

L’aggiunta di una variabile flag può rendere il codice più facile da capire per molti.

Nella condizione che il ciclo interno termini con un’interruzione, imposta il flag su True e nel ciclo esterno, imposta l’interruzione in base al flag.

Doppio anello:

l1 = [1, 2, 3]
l2 = [10, 20, 30]

flag = False
for i in l1:
    for j in l2:
        print(i, j)
        if i == 2 and j == 20:
            flag = True
            print('BREAK')
            break
    if flag:
        break
# 1 10
# 1 20
# 1 30
# 2 10
# 2 20
# BREAK

Triplo ciclo:

l1 = [1, 2, 3]
l2 = [10, 20, 30]
l3 = [100, 200, 300]

flag = False
for i in l1:
    for j in l2:
        for k in l3:
            print(i, j, k)
            if i == 2 and j == 20 and k == 200:
                flag = True
                print('BREAK')
                break
        if flag:
            break
    if flag:
        break
# 1 10 100
# 1 10 200
# 1 10 300
# 1 20 100
# 1 20 200
# 1 20 300
# 1 30 100
# 1 30 200
# 1 30 300
# 2 10 100
# 2 10 200
# 2 10 300
# 2 20 100
# 2 20 200
# BREAK

Puoi evitare i loop nidificati con itertools.product().

Puoi utilizzare itertools.product() per ottenere tutte le combinazioni di più elenchi in un ciclo e ottenere lo stesso risultato dei cicli nidificati.

import itertools

l1 = [1, 2, 3]
l2 = [10, 20, 30]

for i, j in itertools.product(l1, l2):
    print(i, j)
# 1 10
# 1 20
# 1 30
# 2 10
# 2 20
# 2 30
# 3 10
# 3 20
# 3 30

Poiché si tratta di un singolo ciclo, puoi interromperlo nelle condizioni desiderate.

for i, j in itertools.product(l1, l2):
    print(i, j)
    if i == 2 and j == 20:
        print('BREAK')
        break
# 1 10
# 1 20
# 1 30
# 2 10
# 2 20
# BREAK

Aggiungendo l’argomento di itertools.product(), puoi eseguire il processo corrispondente a più cicli multipli.

l1 = [1, 2, 3]
l2 = [10, 20, 30]
l3 = [100, 200, 300]

for i, j, k in itertools.product(l1, l2, l3):
    print(i, j, k)
    if i == 2 and j == 20 and k == 200:
        print('BREAK')
        break
# 1 10 100
# 1 10 200
# 1 10 300
# 1 20 100
# 1 20 200
# 1 20 300
# 1 30 100
# 1 30 200
# 1 30 300
# 2 10 100
# 2 10 200
# 2 10 300
# 2 20 100
# 2 20 200
# BREAK

Nota

In itertools.product(), il processo per l’elemento viene sempre eseguito per tutte le combinazioni.

Nell’esempio seguente, la moltiplicazione viene eseguita 9 volte sia per i che per j.

for i, j in itertools.product(l1, l2):
    x = i * 2 + j * 3
    print(i, j, x)
# 1 10 32
# 1 20 62
# 1 30 92
# 2 10 34
# 2 20 64
# 2 30 94
# 3 10 36
# 3 20 66
# 3 30 96

Nel caso di cicli annidati, il processo per il ciclo esterno viene eseguito dal numero di elementi esterni.

Nell’esempio seguente, la moltiplicazione per la variabile i è solo 3 volte.

for i in l1:
    temp = i * 2
    for j in l2:
        x = temp + j * 3
        print(i, j, x)
# 1 10 32
# 1 20 62
# 1 30 92
# 2 10 34
# 2 20 64
# 2 30 94
# 3 10 36
# 3 20 66
# 3 30 96

Confronto di velocità

Viene mostrato il risultato della misurazione del tempo di esecuzione di ogni modo con il comando magico %%timeit di Jupyter Notebook. Nota che non può essere misurato da eseguire come codice Python.

Si noti che i risultati differiranno a seconda del numero di elementi e del numero di cicli per da annidare.

Prendi un triplo ciclo con 100 elementi come esempio.

import itertools

n = 100
l1 = range(n)
l2 = range(n)
l3 = range(n)

x = n - 1

%%timeit
for i in l1:
    for j in l2:
        for k in l3:
            if i == x and j == x and k == x:
                break
        else:
            continue
        break
    else:
        continue
    break
# 43 ms ± 1.33 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
flag = False
for i in l1:
    for j in l2:
        for k in l3:
            if i == x and j == x and k == x:
                flag = True
                break
        if flag:
            break
    if flag:
        break
# 45.2 ms ± 3.42 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
for i, j, k in itertools.product(l1, l2, l3):
    if i == x and j == x and k == x:
        break
# 55.8 ms ± 458 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

L’uso di else, continue e l’aggiunta di variabili flag sono più o meno equivalenti e itertools.product() è lento.

Tuttavia, in alcuni casi, itertools.product() è più adatto perché migliora la leggibilità del codice anche se è lento. Dovresti usare una seconda della situazione.