Controllo di flusso: if, for, while e comprehension

Se il tuo codice legge sempre le istruzioni dall'alto verso il basso, sta solo eseguendo una lista della spesa. Con if, for e while inizia a decidere. In questo tutorial costruiamo il controllo di flusso da zero, fino alle comprehension, con esempi che puoi eseguire subito.

Hai mai scritto uno script Python che fa esattamente una cosa, dall’inizio alla fine, sempre uguale? Funziona, certo — ma è come un navigatore che conosce una sola strada: al primo cantiere si blocca. Il salto di qualità arriva quando il tuo programma comincia a decidere: se il file esiste aprilo, altrimenti crealo; per ogni riga del dataset controlla il valore; finché l’utente non scrive «esci», continua a chiedere. Questo è il controllo di flusso, e in Python si regge su tre parole chiave — if, for, while — più un’arma segreta tutta pythonica: le comprehension.

Tipi, strutture dati, controllo di flusso e funzioni: le basi di Python sono nel libro, in un percorso che si legge in ordine.

Leggi «Le basi di Python» nel libro →

Prerequisiti minimi: sai eseguire uno script Python 3.12 (se non ricordi come, ti basta un terminale con python script.py dentro un ambiente creato con python -m venv .venv o, più moderno, uv venv) e sai cosa sono liste e stringhe — le abbiamo viste nell’articolo precedente della serie. Tutto il resto lo costruiamo qui.

if: il bivio

Pensa a if come al bivio di una strada: il programma arriva, guarda il cartello (la condizione), e prende una direzione sola. L’analogia quotidiana perfetta è il buttafuori di un locale: guarda il documento, e se hai l’età giusta entri, altrimenti no. Non esiste il «forse»: una condizione in Python è sempre True o False.

# Il buttafuori digitale
eta = 17

if eta >= 18:
    print("Benvenuto!")          # eseguito solo se la condizione è vera
elif eta >= 16:
    print("Solo fino alle 22.")  # controllato solo se la prima è falsa
else:
    print("Torna tra qualche anno.")

Nota due cose. Primo: l’indentazione (i quattro spazi) non è estetica, in Python è la sintassi — il blocco indentato è ciò che viene eseguito. Secondo: elif viene valutato solo se le condizioni precedenti sono false, quindi l’ordine conta. Se avessi messo eta >= 16 per prima, un diciottenne verrebbe trattato da sedicenne.

Python considera «falsi» anche valori che non sono letteralmente False: la lista vuota [], la stringa vuota "", lo zero, None. Per questo vedrai spesso if risultati: invece di if len(risultati) > 0: — dice la stessa cosa, con meno rumore.

for: il nastro trasportatore

Il ciclo for è un nastro trasportatore in una fabbrica: gli oggetti passano uno alla volta davanti a te, e su ciascuno esegui la stessa operazione. Non ti chiedi «a che posizione sono arrivato?»: prendi il prossimo pezzo finché il nastro non si svuota. In Python il for itera direttamente sugli elementi, non sugli indici — è la differenza più grande rispetto a linguaggi come C o Java.

# Controllo qualità su un batch di misure
misure = [12.1, 11.8, 47.9, 12.3, 12.0, 11.9]
SOGLIA = 20.0

anomalie = []
for valore in misure:            # 'valore' assume un elemento alla volta
    if valore > SOGLIA:
        anomalie.append(valore)  # accumulo i fuori soglia

print(f"Trovate {len(anomalie)} anomalie: {anomalie}")
# Trovate 1 anomalie: [47.9]

Questo pattern — parti da un contenitore vuoto, cicli, accumuli — lo userai centinaia di volte: è il cuore della pulizia dati prima di dare in pasto qualsiasi cosa a scikit-learn. Se invece ti serve un ciclo «a conteggio» (fai qualcosa 10 volte), usi range: for i in range(10): genera i numeri da 0 a 9, uno alla volta, senza mai costruire la lista intera in memoria.

while: il termostato

Il for va bene quando sai su cosa iterare. Ma se non sai in anticipo quante volte? Pensa a un termostato: non scalda «per 7 cicli», scalda finché la temperatura non raggiunge quella desiderata. Questo è while: ripeti finché la condizione resta vera.

# Discesa verso un obiettivo: dimezzo l'errore finché non è abbastanza piccolo
errore = 100.0
passi = 0

while errore > 0.1:       # condizione controllata PRIMA di ogni giro
    errore = errore / 2   # se dimentichi questa riga: loop infinito!
    passi += 1

print(f"Convergenza in {passi} passi, errore finale {errore:.4f}")
# Convergenza in 10 passi, errore finale 0.0977

Ti sembra un giochino? È lo scheletro concettuale della discesa del gradiente: ripeti un aggiornamento finché l’errore non scende sotto una tolleranza. Ogni training loop che scriverai in futuro è un while (o un for sulle epoche) travestito. Attenzione al rischio classico: se dentro il ciclo non modifichi mai la condizione, il programma gira per sempre. Due parole chiave ti danno controllo fine: break esce subito dal ciclo, continue salta al giro successivo.

Un programma senza controllo di flusso è una lista della spesa. Con if, for e while diventa una ricetta: assaggia, aggiusta, ripeti finché non è pronto.

Il flowchart di un loop, una volta per tutte

Fissiamo visivamente cosa succede a ogni giro di un ciclo con condizione. Questo schema vale identico per il while ed è, concettualmente, quello che for fa dietro le quinte (la condizione implicita è «ci sono ancora elementi?»).

inizio condizione vera? sì corpo del ciclo aggiorna lo stato no uscita

Il flowchart di un loop: la condizione fa da cancello. Se è vera si esegue il corpo, si aggiorna lo stato e si torna al cancello; se è falsa si esce. Dimenticare l’aggiornamento significa non uscire mai.

Le comprehension: il ciclo in una riga

Ora l’arma segreta. Ricordi il pattern «contenitore vuoto + for + append»? Python lo comprime in un’unica espressione leggibile: la list comprehension. È come ordinare al bar «un caffè macchiato» invece di dettare la ricetta passo passo: stesso risultato, comunicato in un colpo solo.

misure = [12.1, 11.8, 47.9, 12.3, 12.0, 11.9]

# Versione classica: 4 righe
quadrati = []
for m in misure:
    if m < 20:
        quadrati.append(m ** 2)

# Versione comprehension: 1 riga, stesso identico risultato
quadrati = [m ** 2 for m in misure if m < 20]

# Funziona anche per dizionari e set
etichette = {m: ("ok" if m < 20 else "anomalia") for m in misure}
print(quadrati)
print(etichette[47.9])   # anomalia

Si legge così, da sinistra: «costruisci una lista di m**2, per ogni m in misure, ma solo se m < 20«. Nelle pipeline di machine learning le comprehension sono ovunque: filtrare feature, trasformare etichette, costruire vocabolari.

Onestà intellettuale, però: le comprehension non servono sempre. Se dentro il ciclo hai più condizioni annidate, effetti collaterali (scritture su file, print) o più di una trasformazione, il buon vecchio for esplicito è più leggibile — e la leggibilità in Python vince sempre. Una comprehension che devi rileggere tre volte è un ciclo scritto male, non codice elegante. Stesso discorso per i loop in generale: se stai ciclando su numeri per fare matematica elemento per elemento, quasi sempre esiste un’operazione NumPy vettorizzata che fa la stessa cosa cento volte più veloce. Il controllo di flusso è il fondamento, non il martello per ogni chiodo.

Prova tu

Esercizio concreto: scrivi uno script fizzbuzz_dati.py che, data la lista temperature = [18.5, 21.0, 35.2, -3.0, 22.1, 40.5, 19.9], (1) usi un for con if/elif/else per classificare ogni valore in «gelo» (sotto 0), «normale» (0–30) o «allarme» (sopra 30); (2) riscriva la stessa classificazione come dict comprehension {valore: classe}; (3) usi un while che rimuova il valore massimo dalla lista finché la media non scende sotto 20 (ti serve sum(lista)/len(lista) e lista.remove(max(lista))). Se i tre output sono coerenti tra loro, hai capito il controllo di flusso.

Per approfondire: il capitolo «More Control Flow Tools» del tutorial ufficiale di Python copre anche match, il «fratello strutturale» di if introdotto in Python 3.10; e il capitolo 2 di Fluent Python (Luciano Ramalho, 2ª edizione) è la lettura definitiva sulle comprehension fatte bene.

I commenti sono riservati agli iscritti.

Accedi per commentare