1 / 1
Indice della lezione

Capitolo 2 - Controllare il flusso del programma

Nel capitolo precedente abbiamo imparato a rappresentare dati, eseguire calcoli, lavorare con stringhe e acquisire informazioni dall'utente.

Un programma reale, però, deve anche prendere decisioni e ripetere operazioni.

Al termine di questo capitolo sarai in grado di:

  • utilizzare gli operatori di confronto;
  • costruire condizioni con if, elif ed else;
  • comprendere l'indentazione dei blocchi Python;
  • utilizzare l'operatore condizionale;
  • combinare condizioni con and, or e not;
  • comprendere i valori truthy e falsy;
  • capire la short-circuit evaluation;
  • utilizzare i confronti concatenati;
  • iterare con for;
  • utilizzare range() ed enumerate();
  • controllare un ciclo con break e continue;
  • utilizzare for ... else;
  • comprendere gli oggetti iterabili e gli iteratori;
  • utilizzare while;
  • riconoscere e gestire i cicli infiniti;
  • costruire cicli annidati;
  • scegliere tra for e while in base al problema.

2.1 Il flusso di esecuzione

Finora abbiamo scritto programmi in cui le istruzioni venivano eseguite una dopo l'altra:

name = input("Nome: ")
age = int(input("Età: "))
print(f"Ciao {name}, hai {age} anni")

Questo è un flusso sequenziale.

Nella programmazione, però, abbiamo continuamente bisogno di situazioni come:

"Se succede A, fai B; altrimenti fai C."

oppure:

"Ripeti questa operazione per ogni elemento."

Le strutture di controllo servono proprio a modificare il normale flusso lineare del programma.

Le possiamo raggruppare in due grandi categorie:

STRUTTURE DI CONTROLLO
│
├── Decisioni
│   ├── if
│   ├── elif
│   ├── else
│   └── match
│
└── Ripetizioni
    ├── for
    └── while

A queste si aggiungono strumenti come break, continue, confronti concatenati e for ... else.


2.2 Operatori di confronto

Gli operatori di confronto permettono di confrontare due valori.

Il risultato di un confronto è un valore booleano:

True

oppure:

False

I principali operatori sono:

Operatore Significato Esempio


== uguale a 5 == 5 != diverso da 5 != 3 > maggiore di 7 > 5 < minore di 3 < 2 >= maggiore o uguale 5 >= 5 <= minore o uguale 4 <= 6

Esempio:

a = 10
b = 5

print(a > b)
print(a == b)

Output:

True
False

= e ==: attenzione!

È uno degli errori più comuni dei principianti.

age = 18

è un'assegnazione.

age == 18

è un confronto.

Possiamo leggerli così:

=   → assegna
==  → confronta

Confrontare stringhe

Anche le stringhe possono essere confrontate:

print("apple" == "apple")
print("apple" == "Apple")

Risultato:

True
False

Il confronto distingue maiuscole e minuscole.

Le stringhe possono anche essere confrontate in ordine lessicografico:

print("apple" < "banana")

Il risultato è True.

Per i confronti tra stringhe, però, è importante ricordare che Python utilizza i valori Unicode dei caratteri e non un "ordine alfabetico umano" completo.


== non è is

Non bisogna confondere:

  • == → uguaglianza del valore;
  • is → identità, cioè stesso oggetto.

Esempio:

a = [1, 2]
b = [1, 2]

print(a == b)
print(a is b)

Output tipico:

True
False

Le due liste hanno lo stesso contenuto, ma sono due oggetti distinti.

Per ora, quando vogliamo verificare se due valori sono uguali, utilizziamo normalmente ==.


📝 Esercizi --- Operatori di confronto

Esercizio 1

Dato:

x = 10
y = 5

scrivi espressioni che verifichino:

  1. x è maggiore di y;
  2. x è uguale a 10;
  3. y è diverso da 10;
  4. y è minore o uguale a 5.

Esercizio 2

Qual è il risultato?

print(5 == "5")

Spiega perché.

Esercizio 3

Qual è la differenza tra:

x = 10

e:

x == 10

2.3 if: prendere una decisione

L'istruzione if permette di eseguire un blocco di codice solo quando una condizione è vera.

La sintassi è:

if condizione:
    istruzione

Esempio:

age = 20

if age >= 18:
    print("Maggiorenne")

La condizione:

age >= 18

produce True, quindi il print() viene eseguito.

Se invece:

age = 16

la condizione produce False e il blocco non viene eseguito.


2.4 L'indentazione: in Python gli spazi contano

Uno degli aspetti più importanti della sintassi Python è l'indentazione.

Dopo i due punti:

if age >= 18:

il codice appartenente al blocco deve essere indentato:

if age >= 18:
    print("Maggiorenne")

Possiamo avere più istruzioni nello stesso blocco:

if age >= 18:
    print("Maggiorenne")
    print("Puoi accedere")

L'indentazione normalmente utilizzata in Python è di quattro spazi.

Non bisogna pensare all'indentazione come a un semplice elemento grafico. In Python definisce realmente la struttura del programma.

Questo:

if True:
    print("Uno")
    print("Due")

contiene due istruzioni nel blocco if.

Questo:

if True:
    print("Uno")

print("Due")

contiene invece una sola istruzione nel blocco.


📝 Esercizi --- if

Esercizio 4

Scrivi un programma che stampi "Maggiorenne" quando:

age >= 18

Esercizio 5

Scrivi un programma che controlli se un numero è positivo:

number = 10

Esercizio 6

Scrivi un programma che stampi "Disponibile" se:

stock > 0

2.5 if ... else

Spesso non vogliamo gestire soltanto il caso vero, ma anche quello falso.

Per questo utilizziamo else:

if condizione:
    # caso vero
else:
    # caso falso

Esempio:

age = 16

if age >= 18:
    print("Maggiorenne")
else:
    print("Minorenne")

Uno e uno solo dei due blocchi verrà eseguito.

Un esempio con i numeri

number = 7

if number % 2 == 0:
    print("Pari")
else:
    print("Dispari")

Qui utilizziamo ciò che abbiamo imparato nel capitolo precedente:

number % 2

Se il resto è zero, il numero è pari.


📝 Esercizi --- if ... else

Esercizio 7

Chiedi all'utente l'età e stampa:

Maggiorenne

oppure:

Minorenne

Esercizio 8

Chiedi un numero e stampa se è pari o dispari.

Esercizio 9

Dato:

temperature = 30

stampa "Caldo" se la temperatura è almeno 25, altrimenti "Non caldo".


2.6 if ... elif ... else

Quando abbiamo più alternative possiamo usare elif, abbreviazione di else if.

Esempio:

score = 8

if score >= 9:
    print("Ottimo")
elif score >= 7:
    print("Buono")
elif score >= 6:
    print("Sufficiente")
else:
    print("Insufficiente")

Python valuta le condizioni dall'alto verso il basso.

Non appena trova una condizione vera:

  1. esegue il relativo blocco;
  2. ignora gli elif successivi;
  3. ignora l'else.

L'ordine è importante

Consideriamo:

score = 9

if score >= 6:
    print("Sufficiente")
elif score >= 9:
    print("Ottimo")

Il risultato sarà:

Sufficiente

perché score >= 6 è già vero.

Quindi una regola fondamentale è:

Con if / elif / else, l'ordine delle condizioni conta.

È generalmente opportuno mettere prima le condizioni più specifiche o restrittive.


📝 Esercizi --- elif

Esercizio 10

Scrivi una classificazione:

  • >= 90 → "A"
  • >= 80 → "B"
  • >= 70 → "C"
  • >= 60 → "D"
  • altrimenti → "F"

Esercizio 11

Classifica una temperatura:

  • sotto 0 → "Gelo"
  • da 0 a 14 → "Freddo"
  • da 15 a 24 → "Mite"
  • almeno 25 → "Caldo"

Esercizio 12

Spiega perché l'ordine delle condizioni è importante nell'esercizio precedente.


2.7 Condizioni composte

Una condizione può essere costruita combinando più confronti.

Esempio:

age = 30
license = True

if age >= 18 and license:
    print("Può guidare")

La condizione è composta da:

age >= 18

e:

license

entrambe vere.

Possiamo anche scrivere:

number = 8

if number > 0 and number % 2 == 0:
    print("Numero positivo e pari")

Qui stiamo controllando due proprietà contemporaneamente.


2.8 Operatori logici

Gli operatori logici principali sono:

Operatore Significato


and entrambe le condizioni devono essere vere or almeno una condizione deve essere vera not nega una condizione

and

age = 30

print(age >= 18 and age <= 65)

Entrambe le condizioni sono vere, quindi il risultato è True.

or

day = "Sunday"

is_weekend = day == "Saturday" or day == "Sunday"
print(is_weekend)

È sufficiente che una delle due condizioni sia vera.

not

is_closed = False

print(not is_closed)

Risultato:

True

2.9 Precedenza degli operatori logici

Quando compaiono più operatori logici, Python segue questa precedenza:

  1. not
  2. and
  3. or

Quindi:

True or False and False

viene interpretato come:

True or (False and False)

e produce:

True

Quando la logica diventa complessa, usare parentesi è spesso una buona idea:

if (age >= 18 and has_license) or is_professional_driver:
    ...

Le parentesi rendono immediatamente evidente l'intenzione.


2.10 Valori truthy e falsy

In Python una condizione non deve necessariamente essere scritta come:

if valore == True:

Molti oggetti possono essere valutati direttamente in un contesto booleano.

Sono falsy, tra gli altri:

False
None
0
0.0
""
[]
()
{}
set()

Molti altri valori sono truthy.

Esempio:

items = []

if items:
    print("Ci sono elementi")
else:
    print("Lista vuota")

Con:

items = [1, 2, 3]

il primo blocco viene eseguito.

Questo stile è molto comune in Python:

if name:
    print("Nome inserito")

invece di:

if name != "":
    print("Nome inserito")

📝 Esercizi --- Logica booleana

Esercizio 13

Scrivi una condizione che verifichi se:

  • l'età è almeno 18;
  • e la persona possiede una patente.

Esercizio 14

Scrivi una condizione vera quando il giorno è "Saturday" oppure "Sunday".

Esercizio 15

Che cosa viene stampato?

items = []

if items:
    print("A")
else:
    print("B")

Esercizio 16

Indica quali dei seguenti valori sono falsy:

0
"0"
""
[]
[0]
None

2.11 Short-circuit evaluation

La short-circuit evaluation è un comportamento fondamentale di Python.

Quando Python valuta:

A and B

se A è falsy, non ha bisogno di valutare B: il risultato è già determinato.

Analogamente, con:

A or B

se A è truthy, B non deve essere valutato.

Esempio:

False and print("Non verrà eseguito")

La seconda parte non viene eseguita.

Analogamente:

True or print("Non verrà eseguito")

non esegue il secondo print().


2.12 Short-circuit per evitare errori

Questo comportamento è particolarmente utile quando una seconda operazione deve essere eseguita solo se la prima condizione è soddisfatta.

Esempio:

items = []

if items and items[0] == 10:
    print("Il primo elemento è 10")

Python valuta:

items

che è falsy perché la lista è vuota.

Di conseguenza non valuta:

items[0] == 10

e non si verifica IndexError.

Un altro esempio:

user = None

if user and user.is_admin():
    print("Accesso consentito")

Se user è None, Python non tenta di eseguire user.is_admin().


2.13 and e or non restituiscono necessariamente True o False

Questo è un dettaglio molto importante.

Gli operatori and e or in Python restituiscono uno degli operandi, non necessariamente un booleano.

Esempio:

name = "" or "Anonimo"
print(name)

Risultato:

Anonimo

Perché "" è falsy, quindi viene restituito il secondo valore.

Altro esempio:

name = "Mario" or "Anonimo"
print(name)

Risultato:

Mario

Un pattern molto comune è:

config = input("Configurazione: ") or "default"

Se l'utente non inserisce nulla, viene utilizzato "default".

Con and:

token = "abc123" and "OK"
print(token)

risultato:

OK

📝 Esercizi --- Short-circuit

Esercizio 17

Che cosa viene stampato?

print(False and print("Hello"))

Esercizio 18

Che cosa viene stampato?

name = "" or "Guest"
print(name)

Esercizio 19

Scrivi una condizione sicura che acceda al primo elemento di una lista solo quando la lista non è vuota.


2.14 Confronti concatenati

Python permette di scrivere più confronti consecutivi in una forma molto simile alla matematica.

Per esempio:

0 < x < 20

è equivalente a:

0 < x and x < 20

Esempio:

age = 35

if 18 <= age <= 65:
    print("Fascia considerata")

Questo è particolarmente leggibile.

Possiamo concatenare anche più confronti:

0 < x < 10 < y

Python valuta la catena in modo equivalente a:

0 < x and x < 10 and 10 < y

con la particolarità che gli elementi intermedi vengono valutati in modo appropriato senza ripetizioni inutili.

Un errore da evitare

Non scrivere:

18 <= age and age <= 65

se:

18 <= age <= 65

esprime chiaramente la stessa intenzione.

La forma concatenata è uno degli aspetti più eleganti di Python.


📝 Esercizi --- Confronti concatenati

Esercizio 20

Scrivi con un confronto concatenato:

x è maggiore o uguale a 10 e minore di 20

Esercizio 21

Verifica se un voto è compreso tra 6 incluso e 10 incluso.

Esercizio 22

Spiega la differenza tra:

0 < x < 10

e:

0 < x and x < 10

2.15 Operatore condizionale

Python possiede una forma compatta per esprimere una semplice scelta tra due valori:

valore_se_vero if condizione else valore_se_falso

È spesso chiamata operatore ternario, anche se più precisamente si tratta di un'espressione condizionale.

Esempio:

age = 20

status = "Maggiorenne" if age >= 18 else "Minorenne"
print(status)

La versione estesa sarebbe:

if age >= 18:
    status = "Maggiorenne"
else:
    status = "Minorenne"

Un altro esempio

number = 7

parity = "pari" if number % 2 == 0 else "dispari"

Quando usarlo

È utile quando la condizione è breve e il risultato è immediatamente comprensibile.

Da evitare quando la logica diventa complicata:

# difficile da leggere
result = "A" if x > 10 else "B" if x > 5 else "C"

In quel caso è preferibile:

if x > 10:
    result = "A"
elif x > 5:
    result = "B"
else:
    result = "C"

📝 Esercizi --- Espressioni condizionali

Esercizio 23

Crea:

status = "adult" if age >= 18 else "minor"

usando una variabile age.

Esercizio 24

Dato un numero, crea una variabile contenente "pari" oppure "dispari".

Esercizio 25

Riscrivi un'espressione ternaria troppo complessa utilizzando if / elif / else.


2.16 match ... case

A partire da Python 3.10 è disponibile match ... case.

È particolarmente utile quando dobbiamo confrontare un valore con diverse alternative.

Esempio:

choice = int(input("Scegli (1-3): "))

match choice:
    case 1:
        print("Inserisci")
    case 2:
        print("Modifica")
    case 3:
        print("Elimina")
    case _:
        print("Scelta non valida")

Il caso:

case _:

funziona come caso predefinito.

if/elif o match?

Non esiste una regola secondo cui uno debba sempre sostituire l'altro.

Usiamo spesso if quando stiamo valutando condizioni:

if age >= 18:

match è particolarmente naturale quando confrontiamo un valore con alternative discrete:

match command:
    case "start":
        ...
    case "stop":
        ...

📝 Esercizi --- match

Esercizio 26

Crea un menu:

1 - Inserisci
2 - Cerca
3 - Elimina
4 - Esci

e gestiscilo con match.

Esercizio 27

Dato:

day = "Saturday"

usa match per distinguere alcuni giorni della settimana.


2.17 Il ciclo for

Le condizioni permettono di scegliere.

I cicli permettono di ripetere.

Il for viene utilizzato per iterare sugli elementi di un iterabile.

Sintassi:

for variabile in iterabile:
    istruzioni

Esempio:

numbers = [1, 2, 3, 4]

for number in numbers:
    print(number)

Output:

1
2
3
4

Il for non richiede normalmente di gestire manualmente un indice: Python prende un elemento alla volta dall'iterabile e lo assegna alla variabile del ciclo.


2.18 range()

range() è uno degli strumenti più utilizzati nei cicli.

for i in range(5):
    print(i)

Output:

0
1
2
3
4

Attenzione:

range(5) arriva fino a 4: il valore finale 5 è escluso.

Possiamo utilizzare:

range(start, stop)

per esempio:

for i in range(1, 6):
    print(i)

Output:

1
2
3
4
5

Possiamo anche specificare il passo:

for i in range(1, 10, 2):
    print(i)

Output:

1
3
5
7
9

Il passo può essere negativo:

for i in range(5, 0, -1):
    print(i)

Output:

5
4
3
2
1

📝 Esercizi --- for e range()

Esercizio 28

Stampa i numeri da 0 a 9.

Esercizio 29

Stampa i numeri da 1 a 10.

Esercizio 30

Stampa tutti i numeri pari da 2 a 20.

Esercizio 31

Stampa il conto alla rovescia da 10 a 1.


2.19 Iterare sulle stringhe

Una stringa è un iterabile.

Possiamo quindi scrivere:

word = "Python"

for letter in word:
    print(letter)

Output:

P
y
t
h
o
n

Questo è molto utile quando dobbiamo analizzare caratteri singolarmente.

Esempio:

word = "banana"
count = 0

for letter in word:
    if letter == "a":
        count += 1

print(count)

Il risultato è:

3

📝 Esercizi --- Stringhe e cicli

Esercizio 32

Stampa ogni carattere della parola:

word = "Programming"

Esercizio 33

Conta quante volte compare la lettera "a".

Esercizio 34

Conta quante vocali contiene una parola.

Suggerimento:

vowels = "aeiou"

e usa in.


2.20 enumerate()

A volte non ci basta conoscere il valore: vogliamo anche la sua posizione.

Possiamo utilizzare enumerate():

names = ["Anna", "Luca", "Marco"]

for index, name in enumerate(names):
    print(index, name)

Output:

0 Anna
1 Luca
2 Marco

Possiamo modificare l'indice iniziale:

for index, name in enumerate(names, start=1):
    print(index, name)

Output:

1 Anna
2 Luca
3 Marco

Questo è generalmente preferibile a:

for i in range(len(names)):
    print(i, names[i])

quando ci serve semplicemente indice + valore.


📝 Esercizi --- enumerate()

Esercizio 35

Stampa una lista di prodotti numerandoli a partire da 1.

Esercizio 36

Dato:

names = ["Anna", "Luca", "Marco"]

stampa:

1. Anna
2. Luca
3. Marco

2.21 Iterare sui dizionari

Anche un dizionario è iterabile.

Per impostazione predefinita, iterare sul dizionario significa iterare sulle chiavi:

student = {
    "name": "Anna",
    "age": 20
}

for key in student:
    print(key)

Possiamo ottenere i valori:

for value in student.values():
    print(value)

Oppure chiavi e valori:

for key, value in student.items():
    print(key, value)

Questo ultimo schema è molto comune.


📝 Esercizi --- Dizionari

Esercizio 37

Dato:

student = {
    "name": "Anna",
    "age": 20,
    "course": "Python"
}

stampa tutte le chiavi.

Esercizio 38

Stampa tutte le coppie chiave-valore nel formato:

name: Anna
age: 20
course: Python

2.22 break

break interrompe immediatamente il ciclo nel quale si trova.

Esempio:

for number in range(10):
    if number == 5:
        break
    print(number)

Output:

0
1
2
3
4

Quando number diventa 5, viene eseguito break e il ciclo termina.

Caso tipico: ricerca

names = ["Anna", "Luca", "Marco", "Sara"]

for name in names:
    if name == "Marco":
        print("Trovato")
        break

Non è necessario continuare a cercare dopo aver trovato l'elemento.


2.23 continue

continue non termina il ciclo.

Salta invece il resto dell'iterazione corrente e passa alla successiva.

Esempio:

for number in range(5):
    if number == 2:
        continue
    print(number)

Output:

0
1
3
4

Il valore 2 viene saltato.

Un uso tipico è ignorare dati che non ci interessano:

numbers = [1, -2, 3, -4, 5]

for number in numbers:
    if number < 0:
        continue

    print(number)

Output:

1
3
5

📝 Esercizi --- break e continue

Esercizio 39

Scorri:

numbers = [3, 7, 2, 9, 10, 5]

e interrompi il ciclo quando trovi 10.

Esercizio 40

Stampa solo i numeri positivi:

numbers = [3, -2, 5, -8, 10]

usando continue.

Esercizio 41

Qual è la differenza tra break e continue?


2.24 for ... else

Python permette di associare un else a un ciclo for.

L'else viene eseguito quando il ciclo termina normalmente, cioè senza essere interrotto da break.

Esempio:

numbers = [1, 3, 5, 7]

for number in numbers:
    if number % 2 == 0:
        print("Numero pari trovato")
        break
else:
    print("Nessun numero pari")

Non viene trovato alcun numero pari e non viene eseguito break, quindi l'else viene eseguito.

Con:

numbers = [1, 3, 4, 7]

il break viene eseguito e l'else non viene eseguito.

Un modo semplice per ricordarlo

Non pensare:

"else appartiene all'if."

Pensa invece:

"else appartiene al for e viene eseguito se il ciclo non è stato interrotto con break."

Questo costrutto è particolarmente utile per le ricerche.


📝 Esercizi --- for ... else

Esercizio 42

Cerca un numero pari in una lista e stampa:

Numero pari trovato

oppure:

Nessun numero pari

usando for ... else.

Esercizio 43

Cerca la lettera "z" nella parola "python" usando for ... else.


2.25 Gli iterabili

Per capire veramente il for, dobbiamo introdurre il concetto di iterabile.

Un iterabile è un oggetto che può essere attraversato elemento per elemento.

Sono iterabili, tra gli altri:

  • list;
  • tuple;
  • set;
  • dict;
  • str;
  • range;
  • file;
  • generatori.

Per esempio:

for character in "Python":
    print(character)

funziona perché una stringa è iterabile.

Anche:

for number in [1, 2, 3]:
    print(number)

funziona perché una lista è iterabile.


2.26 Iterabile e iteratore

I due termini non sono sinonimi.

Un iterabile è un oggetto dal quale possiamo ottenere un iteratore.

Un iteratore è un oggetto che restituisce gli elementi uno alla volta.

Possiamo osservare il meccanismo:

numbers = [1, 2, 3]

iterator = iter(numbers)

print(next(iterator))
print(next(iterator))
print(next(iterator))

Output:

1
2
3

Dopo l'ultimo elemento, un'ulteriore chiamata a next() produce StopIteration.

Il ciclo for gestisce automaticamente questo meccanismo.

In forma concettuale:

iterabile
   ↓ iter()
iteratore
   ↓ next()
elemento
   ↓
elemento
   ↓
elemento

Per questo non è necessario scrivere manualmente iter() e next() nella maggior parte dei programmi.


2.27 Funzioni che lavorano con gli iterabili

Molte funzioni Python lavorano direttamente con iterabili:

numbers = [1, 2, 3, 4, 5]

print(sum(numbers))
print(min(numbers))
print(max(numbers))

Possiamo anche usare:

any()
all()

Esempio:

numbers = [2, 4, 6]

print(all(number % 2 == 0 for number in numbers))

Risultato:

True

E:

numbers = [1, 3, 4, 7]

print(any(number % 2 == 0 for number in numbers))

Risultato:

True

Queste forme sono già un primo assaggio di strumenti che approfondiremo più avanti.


📝 Esercizi --- Iterabili

Esercizio 44

Indica quali dei seguenti oggetti sono iterabili:

[1, 2, 3]
"Python"
42
range(10)
{"a": 1}

Esercizio 45

Usa sum() per calcolare:

[10, 20, 30, 40]

Esercizio 46

Usa any() per verificare se esiste almeno un numero negativo in:

numbers = [3, 7, -2, 9]

2.28 Il ciclo while

Il ciclo while ripete un blocco finché una condizione è vera.

Sintassi:

while condizione:
    istruzioni

Esempio:

counter = 0

while counter < 5:
    print(counter)
    counter += 1

Output:

0
1
2
3
4

La differenza concettuale rispetto al for è importante.

Il for è normalmente utilizzato quando vogliamo attraversare un iterabile:

for item in items:

Il while è particolarmente adatto quando la fine del ciclo dipende da una condizione:

while password != correct_password:

2.29 Il while e l'aggiornamento della condizione

Un errore molto comune è dimenticare di modificare qualcosa che permetta alla condizione di diventare falsa.

Questo programma è problematico:

counter = 0

while counter < 5:
    print(counter)

counter rimane sempre 0.

La condizione:

counter < 5

rimane quindi sempre vera.

La versione corretta è:

counter = 0

while counter < 5:
    print(counter)
    counter += 1

Quando counter arriva a 5, la condizione diventa falsa e il ciclo termina.


2.30 while e input dell'utente

Un uso tipico del while è ripetere una richiesta finché l'utente non inserisce un valore valido.

Esempio:

password = ""

while password != "python":
    password = input("Inserisci la password: ")

print("Accesso consentito")

Il numero di tentativi non è noto in anticipo.

Questo è un caso naturale per while.

Una versione più robusta, che limita i tentativi, sarà possibile quando approfondiremo ulteriormente condizioni e gestione degli errori.


2.31 while con break

Possiamo utilizzare un ciclo apparentemente infinito e decidere esplicitamente quando uscire:

while True:
    command = input("Comando: ")

    if command == "exit":
        break

    print(f"Hai scritto: {command}")

Questo schema è molto comune nei programmi interattivi.

Il significato è:

continua a chiedere comandi finché l'utente non inserisce exit.


2.32 while con continue

Anche while supporta continue.

counter = 0

while counter < 5:
    counter += 1

    if counter == 3:
        continue

    print(counter)

Output:

1
2
4
5

È importante aggiornare il contatore prima del continue; altrimenti potremmo creare un ciclo infinito.


2.33 while ... else

Anche while può avere un else.

L'else viene eseguito quando il ciclo termina normalmente, senza break.

counter = 0

while counter < 3:
    print(counter)
    counter += 1
else:
    print("Ciclo terminato normalmente")

Output:

0
1
2
Ciclo terminato normalmente

Se invece il ciclo viene interrotto:

counter = 0

while counter < 3:
    if counter == 1:
        break

    print(counter)
    counter += 1
else:
    print("Ciclo terminato normalmente")

l'else non viene eseguito.


📝 Esercizi --- while

Esercizio 47

Stampa i numeri da 1 a 10 usando while.

Esercizio 48

Scrivi un programma che continui a chiedere un comando finché l'utente non scrive:

exit

Esercizio 49

Utilizza while per calcolare la somma dei numeri da 1 a 100.

Esercizio 50

Spiega perché questo ciclo non termina:

i = 0

while i < 5:
    print(i)

2.34 Cicli infiniti

Un ciclo infinito è un ciclo che non termina.

Il caso più esplicito è:

while True:
    print("Infinito")

La condizione è sempre vera.

Un ciclo infinito non è necessariamente un errore.

Può essere intenzionale in:

  • server;
  • programmi interattivi;
  • sistemi che attendono continuamente eventi;
  • loop principali di alcune applicazioni.

Per esempio:

while True:
    command = input("Comando: ")

    if command == "exit":
        break

Il ciclo è infinito solo fino a quando non viene ricevuto il comando di uscita.

Ciclo infinito accidentale

Un errore tipico è:

i = 0

while i < 5:
    print(i)

Manca:

i += 1

quindi la condizione non cambia mai.


2.35 Come interrompere un ciclo

Possiamo interrompere un ciclo in diversi modi.

Nel contesto che stiamo studiando:

break

while True:
    if condition:
        break

return

All'interno di una funzione, return termina la funzione e quindi anche il ciclo che si trova al suo interno.

Ctrl + C

Durante l'esecuzione da terminale possiamo normalmente interrompere un programma con:

Ctrl + C

che genera un'interruzione, generalmente visibile come KeyboardInterrupt.


📝 Esercizi --- Cicli infiniti

Esercizio 51

Individua il problema:

i = 1

while i <= 10:
    print(i)

Esercizio 52

Trasforma:

while True:
    ...

in un menu che termini quando l'utente sceglie 0.


2.36 Cicli annidati

Un ciclo può contenere un altro ciclo.

Si parla di cicli annidati o nested loops.

Esempio:

for i in range(3):
    for j in range(2):
        print(i, j)

Output:

0 0
0 1
1 0
1 1
2 0
2 1

Per ogni valore di i, il ciclo interno viene eseguito completamente.

Possiamo visualizzarlo così:

i = 0 → j = 0, 1
i = 1 → j = 0, 1
i = 2 → j = 0, 1

2.37 Tabelle e matrici

I cicli annidati sono particolarmente utili per strutture a righe e colonne.

Esempio:

matrix = [
    [1, 2, 3],
    [4, 5, 6]
]

for row in matrix:
    for value in row:
        print(value)

Output:

1
2
3
4
5
6

Possiamo anche visualizzare una tabella:

for i in range(1, 6):
    for j in range(1, 6):
        print(i * j, end="\t")
    print()

Il secondo print() serve per andare a capo dopo ogni riga.


2.38 break nei cicli annidati

Attenzione: un break interrompe il ciclo più interno nel quale viene eseguito.

Esempio:

for i in range(3):
    for j in range(3):
        if j == 1:
            break
        print(i, j)

Il break interrompe il ciclo su j, non quello su i.

Questa distinzione è importante quando i cicli diventano più complessi.


2.39 Complessità dei cicli annidati

Se un ciclo esegue n operazioni e contiene un secondo ciclo che esegue n operazioni per ogni iterazione, possiamo avere circa:

n × n = n²

operazioni.

In termini di complessità:

un ciclo     → O(n)
due cicli    → O(n²)
tre cicli    → O(n³)

Non significa che ogni programma con due for sia automaticamente O(n²): la complessità reale dipende da ciò che fanno i cicli e dalle dimensioni degli input.

Il concetto da ricordare è:

I cicli annidati possono far crescere rapidamente il lavoro necessario al programma.


📝 Esercizi --- Cicli annidati

Esercizio 53

Utilizza due cicli per stampare tutte le coppie:

(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)

Esercizio 54

Costruisci una tabella 5 × 5 contenente i prodotti tra righe e colonne.

Esercizio 55

Data:

matrix = [
    [1, 2],
    [3, 4],
    [5, 6]
]

stampa tutti gli elementi usando cicli annidati.


2.40 Scegliere tra for e while

Una domanda importante è:

Quando devo utilizzare for e quando while?

Una regola pratica è:

Usa for quando devi attraversare un iterabile

for product in products:
    print(product)

oppure quando il numero di iterazioni è definito:

for i in range(10):
    ...

Usa while quando la ripetizione dipende da una condizione

while balance > 0:
    ...

oppure quando non sai in anticipo quante iterazioni saranno necessarie:

while command != "exit":
    command = input("Comando: ")

Non esiste però una regola assoluta.

Lo stesso problema può spesso essere risolto con entrambi i costrutti. La scelta migliore è quella che rende più evidente l'intenzione del programma.


2.41 Un esempio completo: analisi di una lista

Possiamo ora combinare condizioni e cicli.

Partiamo da:

numbers = [3, 7, 2, 9, 4, 10, 5]

Pari e dispari

for number in numbers:
    if number % 2 == 0:
        print(number, "pari")
    else:
        print(number, "dispari")

Contare i numeri maggiori di 5

count = 0

for number in numbers:
    if number > 5:
        count += 1

print("Numeri > 5:", count)

Cercare un numero maggiore di 8

found = False

for number in numbers:
    if number > 8:
        found = True
        break

print("Esiste un numero > 8:", found)

Possiamo anche utilizzare any():

found = any(number > 8 for number in numbers)

Entrambe le soluzioni sono corrette. La prima è utile per comprendere il meccanismo dei cicli; la seconda è più compatta.

Verificare che non esistano numeri negativi

for number in numbers:
    if number < 0:
        print("Numero negativo trovato")
        break
else:
    print("Nessun numero negativo")

Sommare con while

index = 0
total = 0

while index < len(numbers):
    total += numbers[index]
    index += 1

print("Somma totale:", total)

In questo caso sum(numbers) sarebbe naturalmente più semplice, ma il while ci permette di esercitarci sul controllo manuale dell'indice.


📝 Esercizi --- Applicazione

Esercizio 56

Data:

numbers = [3, 7, 2, 9, 4, 10, 5]

calcola quanti numeri sono pari.

Esercizio 57

Trova il primo numero maggiore di 8 utilizzando break.

Esercizio 58

Verifica con for ... else se nella lista sono presenti numeri negativi.

Esercizio 59

Calcola la somma usando while.


2.42 Quiz di autovalutazione

Prova a rispondere prima di guardare le soluzioni.

Domanda 1

Qual è il risultato?

print(5 == "5")
  • True
  • False

Domanda 2

Qual è il risultato?

x = 10
print(0 < x < 20)
  • True
  • False

Domanda 3

Quale messaggio viene visualizzato?

print(False and print("Hello"))
  • Hello
  • nulla

Domanda 4

Qual è il risultato?

x = 0
print(x or 10)
  • 0
  • False
  • 10

Domanda 5

Qual è il risultato?

age = 17
status = "OK" if age >= 18 else "NO"
print(status)
  • OK
  • NO

Domanda 6

Qual è il risultato?

print(not (True or False))
  • True
  • False

Domanda 7

Qual è il risultato?

a = 5
b = 10

print(a < b == 10)
  • True
  • False

Soluzioni

  1. False: un intero e una stringa non sono lo stesso valore.
  2. True.
  3. Non viene stampato Hello: and si ferma sul primo valore falsy.
  4. 10: 0 è falsy.
  5. NO.
  6. False.
  7. True: equivale a a < b and b == 10.

Autovalutazione

  • 7 corrette → ottima padronanza;
  • 5--6 corrette → buona comprensione;
  • 3--4 corrette → ripassa condizioni e operatori;
  • 0--2 corrette → riparti dagli esempi e svolgi nuovamente gli esercizi.

2.43 Sfida finale --- Analizzatore numerico

È arrivato il momento di combinare tutto ciò che abbiamo studiato.

Realizza un programma che analizzi una serie di numeri.

Partiamo da:

numbers = [3, 7, 2, 9, 4, 10, 5]

Requisiti

Il programma deve:

  1. attraversare la lista con un for;
  2. indicare per ogni numero se è pari o dispari;
  3. contare quanti numeri sono maggiori di 5;
  4. verificare se esiste almeno un numero maggiore di 8;
  5. verificare con for ... else che non esistano numeri negativi;
  6. calcolare la somma utilizzando while;
  7. interrompere un ciclo quando incontra il numero 10;
  8. visualizzare un riepilogo finale.

Esempio di output

=== ANALISI NUMERI ===

3 → dispari
7 → dispari
2 → pari
9 → dispari
4 → pari
10 → pari
5 → dispari

Numeri maggiori di 5: 3
Esiste un numero maggiore di 8: True
Nessun numero negativo
Somma totale: 40
Trovato 10: ciclo interrotto

Vincoli

Per questa versione utilizza:

  • if / elif / else;
  • for;
  • while;
  • break;
  • continue dove opportuno;
  • for ... else;
  • operatori di confronto;
  • operatori logici.

Puoi utilizzare anche len(), any() e sum(), ma prova prima a risolvere il problema con i cicli per esercitarti.


2.44 Soluzione guidata della sfida

Una possibile soluzione è:

numbers = [3, 7, 2, 9, 4, 10, 5]

print("=== ANALISI NUMERI ===")
print()

# 1–2. Pari o dispari
for number in numbers:
    if number % 2 == 0:
        print(f"{number} → pari")
    else:
        print(f"{number} → dispari")

# 3. Conteggio dei numeri > 5
count = 0

for number in numbers:
    if number > 5:
        count += 1

print()
print(f"Numeri maggiori di 5: {count}")

# 4. Verifica dell'esistenza di un numero > 8
found = False

for number in numbers:
    if number > 8:
        found = True
        break

print(f"Esiste un numero maggiore di 8: {found}")

# 5. Verifica dei numeri negativi
for number in numbers:
    if number < 0:
        print("Numero negativo trovato")
        break
else:
    print("Nessun numero negativo")

# 6. Somma con while
index = 0
total = 0

while index < len(numbers):
    total += numbers[index]
    index += 1

print(f"Somma totale: {total}")

# 7. Interruzione quando viene trovato 10
for number in numbers:
    if number == 10:
        print("Trovato 10: ciclo interrotto")
        break

Una seconda soluzione più idiomatica

Dopo aver compreso bene i cicli, alcune operazioni possono essere espresse in modo più compatto:

numbers = [3, 7, 2, 9, 4, 10, 5]

count_greater_than_5 = sum(number > 5 for number in numbers)
has_number_greater_than_8 = any(number > 8 for number in numbers)
total = sum(numbers)

print(f"Numeri maggiori di 5: {count_greater_than_5}")
print(f"Esiste un numero maggiore di 8: {has_number_greater_than_8}")
print(f"Somma totale: {total}")

Qui compare un concetto interessante: in Python bool è un sottotipo di int, quindi True e False possono essere utilizzati numericamente in contesti come questo. Tuttavia, questa forma va utilizzata solo quando rende il codice realmente più chiaro.

La prima soluzione rimane fondamentale dal punto di vista didattico perché mostra esplicitamente come si costruiscono i risultati.


2.45 Riepilogo del capitolo

In questo capitolo abbiamo imparato a controllare il flusso dei programmi Python.

Abbiamo visto che:

  • gli operatori di confronto producono valori booleani;
  • = serve per l'assegnazione, mentre == confronta due valori;
  • if permette di eseguire codice condizionatamente;
  • elif permette di gestire più alternative;
  • else gestisce il caso alternativo;
  • l'indentazione definisce i blocchi di codice Python;
  • and, or e not permettono di combinare condizioni;
  • Python possiede valori truthy e falsy;
  • la short-circuit evaluation evita valutazioni non necessarie;
  • and e or possono restituire gli operandi e non necessariamente True o False;
  • i confronti possono essere concatenati;
  • l'espressione condizionale permette di scrivere scelte semplici in forma compatta;
  • match ... case è utile per confrontare un valore con diverse alternative;
  • for permette di attraversare un iterabile;
  • range() genera una sequenza di valori utilizzabile nei cicli;
  • enumerate() permette di ottenere indice e valore;
  • break interrompe un ciclo;
  • continue salta l'iterazione corrente;
  • for ... else permette di gestire il caso in cui una ricerca termini senza break;
  • gli iterabili possono essere attraversati elemento per elemento;
  • gli iteratori forniscono gli elementi progressivamente tramite next();
  • while ripete un blocco finché una condizione rimane vera;
  • un ciclo infinito può essere intenzionale oppure derivare da un errore;
  • i cicli annidati permettono di lavorare con strutture a più dimensioni;
  • la scelta tra for e while dipende dal problema che vogliamo risolvere.

Abbiamo quindi aggiunto al nostro modello mentale un elemento fondamentale:

Capitolo 1
DATI
   ↓
variabili → tipi → operatori → stringhe
   ↓
Capitolo 2
CONTROLLO DEL FLUSSO
   ↓
decisioni → ripetizioni → ricerca → iterazione

Nel prossimo capitolo potremo utilizzare queste strutture per lavorare con una delle caratteristiche più importanti di Python: le collezioni di dati, iniziando dalle liste e imparando a creare, modificare e attraversare strutture dati sempre più ricche.

← → per navigare F fullscreen