Il ciclo for in Python, “for loop” in inglese, viene uti­liz­za­to per eseguire ri­pe­tu­ta­men­te un blocco di codice. I cicli for sono una parte fon­da­men­ta­le della maggior parte dei linguaggi di pro­gram­ma­zio­ne. Vi mostriamo come funziona il ciclo for su Python e come uti­liz­zar­lo.

Registra il tuo dominio
  • Domain Connect gratuito per una con­fi­gu­ra­zio­ne facile del DNS
  • Cer­ti­fi­ca­to SSL Wildcard gratuito
  • Pro­te­zio­ne privacy inclusa

Cos’è il ciclo for su Python?

Oltre all’istru­zio­ne if-else, il ciclo for è pro­ba­bil­men­te la struttura di pro­gram­ma­zio­ne più co­no­sciu­ta. Ge­ne­ra­zio­ni di stu­den­tes­se e studenti si sono scer­vel­la­te sul concetto in­for­ma­ti­co di ciclo, in quanto ini­zial­men­te non risulta molto intuitivo. Tuttavia, i problemi di com­pren­sio­ne possono derivare più che altro dal modo in cui il materiale viene pre­sen­ta­to. Di per sé, infatti, i cicli non sono nulla di ec­ce­zio­na­le.

Vi il­lu­stria­mo il concetto di ciclo con un esempio. Im­ma­gi­nia­mo una classe delle ele­men­ta­ri. L’in­se­gnan­te vuole de­ter­mi­na­re l’altezza media delle bambine e dei bambini con un espe­ri­men­to. Per farlo, chiede a ogni bambina e bambino la loro altezza e somma le singole altezze per ottenere un totale. Poi divide la somma per il numero degli alunni della classe per ricavare l’altezza media. Astraiamo un semplice algoritmo dalla procedura dell’in­se­gnan­te:

  1. Richiesta dell’altezza di ciascun bambino e bambina e aggiunta al totale corrente
  2. Divisione della somma per il numero di alunni della classe

Il primo passaggio viene eseguito una volta per ogni bambino e bambina e dipende quindi dal numero di studenti nella classe. Il secondo viene eseguito una sola volta, in­di­pen­den­te­men­te dalle di­men­sio­ni della classe. Abbiamo bisogno di un ciclo per il primo passaggio. Di seguito ri­por­tia­mo quindi un esempio di codice che calcola l’altezza media di un bambino o una bambina della scuola con un ciclo for su Python:

children_heights = [155, 171, 148, 161, 158, 153, 162]
height_sum = 0
for height in children_heights:
    height_sum = height_sum + height
average_height = height_sum // len(children_heights)
print(average_height)

Il ciclo for su Python esegue ri­pe­tu­ta­men­te un blocco di codice. Questa ope­ra­zio­ne viene anche definita “ite­ra­zio­ne”. In par­ti­co­la­re, i cicli con­sen­to­no a diversi elementi combinati in un “insieme” di essere elaborati sin­go­lar­men­te secondo lo stesso schema. Si usa un ciclo for quando la di­men­sio­ne dell’insieme può essere de­ter­mi­na­ta in fase di ese­cu­zio­ne del programma. In caso contrario, di solito si utilizza un ciclo while.

Consiglio

Imparate da soli a scrivere nel codice Python grazie al nostro tutorial su Python.

Cosa distingue il ciclo for su Python dagli altri linguaggi?

Molti linguaggi di pro­gram­ma­zio­ne conoscono il concetto di ciclo for. Oltre che in Python, è una struttura di base anche in altri linguaggi come C, Java, Ja­va­Script e PHP. I linguaggi puramente fun­zio­na­li come Haskell o Lisp di solito riescono a fare a meno di un ciclo for esplicito. Al posto dell’ite­ra­zio­ne, in questi linguaggi si uti­liz­za­no funzioni ricorsive.

Tutti i cicli hanno in comune il fatto che un blocco di codice viene eseguito ri­pe­tu­ta­men­te. Tuttavia, il mec­ca­ni­smo d’azione del ciclo for in Python dif­fe­ri­sce no­te­vol­men­te da quello di altri linguaggi. Ad esempio, la maggior parte dei linguaggi di pro­gram­ma­zio­ne utilizza una co­sid­det­ta variabile di ciclo che viene in­cre­men­ta­ta o de­cre­men­ta­ta durante l’ese­cu­zio­ne del ciclo.

Ope­ra­zio­ne Si­gni­fi­ca­to Sintassi con­ven­zio­na­le Sintassi Python
In­cre­men­to Aumenta il valore di una variabile intera di una quantità specifica e fissa i++ index += 1
De­cre­men­to Di­mi­nui­sce il valore di una variabile intera di una quantità specifica e fissa i-- index -= 1

Vediamo un esempio di come funziona un ciclo for in altri linguaggi. Con un ciclo for, pro­du­cia­mo i numeri da 0 a 9 in Ja­va­Script. Definiamo una variabile intera “number” e la in­cre­men­tia­mo finché il numero in essa contenuto è inferiore a 10. Il codice uti­liz­za­to a questo scopo sembra piuttosto criptico per i nuovi arrivati:

for ( let number = 0; number < 10; number++ ) {
    console.log(number);
}

Il codice per un ciclo for cor­ri­spon­den­te in Python appare molto più ordinato:

for number in range(10):
    print(number)

Invece di fornire di­ret­ta­men­te il valore della variabile del ciclo, di solito viene uti­liz­za­to per in­di­ciz­za­re un elemento all’interno di un insieme. Di nuovo, prima un esempio in Ja­va­Script: pro­du­cia­mo i nomi contenuti nell’elenco “people” uno dopo l’altro. Uti­liz­zia­mo la variabile del ciclo ‘i’ come indice pro­gres­si­vo dei singoli elementi:

people = ['Jack', 'Jim', 'John']
for (let i = 0; i < people.length; i++) {
    console.log("Here comes " + people[i]);
}

L’in­di­ciz­za­zio­ne diretta di elementi suc­ces­si­vi dell’elenco deve essere trattata con cautela. Questo perché un tentativo di accesso al di fuori dei limiti con­sen­ti­ti provoca un errore di runtime. Di norma, si tratta del famoso “off-by-one error”. Python dimostra che si può procedere in modo diverso. Qui vi pro­po­nia­mo lo stesso esempio con un ciclo for su Python, in cui iteriamo di­ret­ta­men­te gli elementi dell’elenco senza in­di­ciz­zar­li con una variabile del ciclo:

people = ['Jack', 'Jim', 'John']
for person in people:
    print(f"Here comes {person}")

L’uso indiretto della variabile del ciclo con­tri­bui­sce a creare molta con­fu­sio­ne quando si imparano i cicli for in altri linguaggi. Questo perché di solito non siamo in­te­res­sa­ti alla variabile centrale del ciclo. Viene uti­liz­za­ta solo per in­di­ciz­za­re i singoli elementi. L’uso dei cicli for con­ven­zio­na­li richiede la com­pren­sio­ne di diverse questioni complesse. Vediamone un esempio con Ja­va­Script:

Argomento Oc­cor­ren­za nel ciclo for di Ja­va­Script
As­se­gna­zio­ne delle variabili let i = 0
Espres­sio­ni booleane i < limit
Operatore di in­cre­men­to o de­cre­men­to i++ / i--
De­ter­mi­na­zio­ne delle di­men­sio­ni di un insieme i < list.length
In­di­ciz­za­zio­ne degli elementi basata su zero i < list.length ist OK; i <= list.length führt zu Off-by-one Error

Per chiarire meglio, vediamo il risultato delle singole lettere di una parola. All’interno del ciclo for, una lettera della parola viene resa di­spo­ni­bi­le nella variabile “letter” per ogni passaggio del ciclo. Questo funziona senza una variabile di ciclo e quindi senza il rischio di produrre un errore off-by-one. Il codice è preciso e di facile lettura:

word = "Python"
for letter in word:
    print(letter)

Come funziona un ciclo for in Python?

Come abbiamo visto, il ciclo for su Python risolve ele­gan­te­men­te il problema dell’ite­ra­zio­ne degli elementi di un insieme. Possiamo farlo senza de­via­zio­ni at­tra­ver­so una variabile numerica di ciclo. È si­cu­ra­men­te una buona cosa, ma come funziona esat­ta­men­te? Per com­pren­de­re il principio di fun­zio­na­men­to del ciclo for in Python, è ne­ces­sa­rio conoscere i concetti di iterabile e iteratore.

Cosa sono l’iterabile, l’iteratore e il ge­ne­ra­to­re?

Il ciclo for in Python opera su oggetti noti come “iterabili”. In questo caso si tratta di stringhe, liste, tuple e altri tipi di dati composti. Usando le parole della do­cu­men­ta­zio­ne ufficiale di Python:

Citazione

“[An iterable is] an object capable of returning its members one at a time” - Fonte: https://docs.python.org/3/glossary.html#term-iterable

Tra­du­zio­ne: “[Un iterabile è] un oggetto che ha la capacità di re­sti­tui­re i suoi membri uno alla volta” (tradotto da IONOS).

Un oggetto iterabile ha due proprietà:

  1. Raggruppa diversi elementi in un insieme.
  2. Ga­ran­ti­sce l’accesso agli elementi tramite un’in­ter­fac­cia chiamata “iteratore”.

Nel complesso, ciò significa che gli iterabili sono insiemi il cui contenuto può essere iterato. In par­ti­co­la­re, un iterabile ha un metodo “__iter__()” che re­sti­tui­sce un iteratore. L’iteratore è un oggetto che re­sti­tui­sce l’elemento suc­ces­si­vo dell’iterabile su comando. Inoltre, un iteratore ricorda la posizione dell’ultimo elemento re­sti­tui­to all’interno dell’insieme.

Funzione Spie­ga­zio­ne Esempio
iter(col­lec­tion) Richiama il metodo __iter__() dell’insieme it = iter("Python")
next(iter) Richiama il metodo __next__() dell’iteratore next(it)
col­lec­tion[index] Richiama il metodo __getitem__(index) dell’insieme 'Python'[1]

Un iteratore re­sti­tui­sce l’elemento suc­ces­si­vo quando viene ri­chia­ma­to il metodo __next__(). Se tutti gli elementi dell’insieme sono stati con­se­gna­ti, l’iteratore è esaurito. Un’altra chiamata a __next__() attiva un’eccezione “Sto­pI­te­ra­tion”.

Vediamo come funziona un iteratore con un esempio. Creiamo un oggetto range() che rap­pre­sen­ta i numeri con­se­cu­ti­vi da 21 a 23. Suc­ces­si­va­men­te, creiamo un iteratore con la funzione iter() e otteniamo gli elementi suc­ces­si­vi con la funzione next(). Con l’ultima chiamata, viene avviata l’eccezione perché l’iteratore è esaurito:

numbers = range(21, 24)
number = iter(numbers)
next(number)
# returns `21`
next(number)
# returns `22`
next(number)
# returns `23`
next(number)
# raises `StopIteration` exception

Un iteratore consente di accedere ai singoli elementi di un insieme. Python conosce anche il concetto correlato di “ge­ne­ra­to­re”. La dif­fe­ren­za è che un ge­ne­ra­to­re crea i singoli elementi solo quando vi si accede. In questo modo si risparmia spazio in memoria durante l’ese­cu­zio­ne del programma; questa ope­ra­zio­ne viene definita anche “lazy ge­ne­ra­tion”.

Un ge­ne­ra­to­re in Python si basa su una funzione che utilizza l’istru­zio­ne yield. Simile all’istru­zio­ne return, re­sti­tui­sce un oggetto e termina la chiamata di funzione. Quando viene ri­chia­ma­ta, tuttavia, la funzione ge­ne­ra­to­re non riparte dall’inizio, ma continua dopo l’ultima istru­zio­ne yield.

Vediamo un esempio. Scriviamo la nostra im­ple­men­ta­zio­ne della funzione range(). Per farlo, uti­liz­zia­mo l’istru­zio­ne yield all’interno di un ciclo while per generare numeri pro­gres­si­vi:

def my_range(start, stop):
    if stop < start:
        return None
    current = start
    while current < stop:
        yield current
        current += 1
# test
assert list(my_range(7, 9)) == list(range(7, 9))

Saltare e annullare un ciclo for in Python

In pratica, a volte è ne­ces­sa­rio saltare un singolo passaggio del ciclo. Come molti altri linguaggi, Python contiene l’istru­zio­ne continue. Quando viene ri­chia­ma­to continue all’interno del corpo del ciclo, l’ite­ra­zio­ne corrente viene in­ter­rot­ta. Il ciclo inizia im­me­dia­ta­men­te l’ite­ra­zio­ne suc­ces­si­va.

L’istru­zio­ne continue può essere uti­liz­za­ta in modo simile a quella Early Return in una chiamata di funzione. Ad esempio, saltiamo un’ite­ra­zio­ne non appena sta­bi­lia­mo che un set di dati non ha la qualità richiesta:

def process_data(data):
    for data_set in data:
        data_set.validate()
        # early continue after cheap check fails
        if not data_set.quality_ok():
            continue
        # expensive operation guarded by early continue
        data_set.process()

Un altro esempio. Pro­du­cia­mo un testo e saltiamo tutte le lettere in seconda posizione:

text = 'Skipping every second letter'
for index, letter in enumerate(text):
    if index % 2 != 0 and letter != ' ':
        continue
    print(letter)

L’istru­zio­ne break viene spesso uti­liz­za­ta per im­ple­men­ta­re algoritmi di ricerca. Se un elemento cercato è stato trovato all’interno di un ciclo, non è ne­ces­sa­rio iterare ul­te­rior­men­te. Ana­lo­ga­men­te alla funzione any(), con­trol­lia­mo un elenco per ve­ri­fi­ca­re la presenza di un singolo valore true. In­ter­rom­pia­mo con break non appena abbiamo trovato qualcosa:

bool_list = [False, False, True, False]
for index, boolean in enumerate(bool_list):
    if boolean:
        print(f"Value at position {index + 1} is True")
        print(f"Aborting inspection of remaining {len(bool_list) - index - 1} item(s)")
        break

In relazione all’istru­zio­ne break, un ciclo for in Python può essere dotato di un corpo opzionale else. Il codice che contiene viene eseguito quando il ciclo termina senza che sia stata eseguita un’istru­zio­ne break:

def find_element(target, collection):
    for element in collection:
        if element == target:
            print("Found what you're looking for")
            break
    else:
        print("Didn't find what you were looking for")
# test
find_element('a', 'Python')
find_element('o', 'Python')

I cicli for sono spesso uti­liz­za­ti in Python all’interno dei corpi delle funzioni. In questo caso, è comune uti­liz­za­re un’istru­zio­ne return invece di una break. Il nostro algoritmo di ricerca si riformula senza l’uso di break ed else:

def find_element(target, collection):
    for element in collection:
        if element == target:
            print("Found what you're looking for")
            # returning breaks us out of the loop
            return element
    # we made it here without returning
    print("Didn't find what you were looking for")
    return None
# test
print(find_element('a', 'Python'))
print(find_element('o', 'Python'))

Quali sono le migliori pratiche per i cicli for in Python?

I cicli for in Python sono usati prin­ci­pal­men­te per iterare gli elementi di una sequenza o di un insieme. Inoltre, per molti casi d’uso comuni esistono metodi più diretti. Vi pre­sen­tia­mo le migliori pratiche prin­ci­pa­li e anti-pattern. In­nan­zi­tut­to, un riepilogo dei termini centrali:

Termine Spie­ga­zio­ne Esempio
Insieme Riassunto di più elementi. Un insieme è un iterabile ('Walter', 'White'), [4, 2, 6, 9], 'Python'
Iteratore In­ter­fac­cia per iterare gli insiemi it = iter('Python')
Ge­ne­ra­to­re Una funzione che utilizza yield al posto dell’istru­zio­ne return. Un ge­ne­ra­to­re è un iterabile range(10)
Com­pren­sio­ne Espres­sio­ne iterante; genera un nuovo insieme basato su un iterabile [num ** 2 for num in range(10)]

Ite­ra­zio­ne diretta degli elementi di un insieme

Un errore comune dei pro­gram­ma­to­ri Python inesperti è l’uso improprio del ciclo for in Python. Come è comune in altri linguaggi, si usa la funzione len() come limite di quella range() per creare una variabile numerica del ciclo. In questo modo in­di­ciz­za­te i singoli elementi dell’insieme:

word = 'Python'
for i in range(len(word)):
    print(word[i])

Questo anti-pattern è di­sap­pro­va­to in quanto non è con­si­de­ra­to in linea con la sintassi di Python per una buona ragione. Infatti, è meglio iterare di­ret­ta­men­te gli elementi dell’insieme con il ciclo for in Python:

word = 'Python'
for letter in word:
    print(letter)

Enumerare gli elementi di un insieme con enumerate() in­clu­den­do l’indice

A volte è ne­ces­sa­rio l’indice di un elemento all’interno dell’insieme. Invece di creare l’indice come variabile del ciclo, uti­liz­zia­mo la funzione enumerate(). Viene così re­sti­tui­ta la tupla (indice, elemento). Si noti che l’indice inizia a contare da zero:

names = ["Jim", "Jack", "John"]
for index, name in enumerate(names):
    print(f"{index + 1}. {name}")

Ite­ra­zio­ne di tuple di elementi con la funzione zip()

Un altro scenario comune è quello di iterare con­tem­po­ra­nea­men­te gli elementi di due insiemi di uguale lunghezza. L’approccio di Python utilizza la funzione zip(), prendendo due insiemi di uguale lunghezza e re­sti­tuen­do 2 tuple suc­ces­si­ve:

people = ('Jim', 'Jack', 'John')
ages = (42, 69, 13)
# ascertain both collections are same length
assert len(people) == len(ages)
# iterate over tuples of (person, age)
for person, age in zip(people, ages):
    print(f"{person} is {age} years old")

Creazione di una variabile di ciclo numerica con la funzione range()

Nor­mal­men­te, i cicli for vengono uti­liz­za­ti in Python per iterare gli elementi di un insieme. L’in­cre­men­to di un intero con un ciclo for è un caso par­ti­co­la­re in Python. Il modo corretto è quello di costruire un oggetto range con la funzione range() e iterarlo:

for counter in range(10):
    print(counter)

Usare l’operatore in per ve­ri­fi­ca­re se un insieme contiene un elemento

Trovare un elemento par­ti­co­la­re all’interno di un insieme fa parte del re­per­to­rio standard di un pro­gram­ma­to­re o di una pro­gram­ma­tri­ce. Nor­mal­men­te, si utilizza una funzione che itera gli elementi, ve­ri­fi­can­do l’ugua­glian­za di ciascun elemento con quello cercato. Se l’elemento viene trovato, l’ite­ra­zio­ne viene in­ter­rot­ta.

In Python, per questo caso comune esiste l’operatore in. L’operatore verifica se l’insieme contiene l’elemento cercato e re­sti­tui­sce un valore booleano cor­ri­spon­den­te:

'a' in 'Python'
'y' in 'Python'

Utilizzo della funzione list() per creare un elenco da un iterabile

A dif­fe­ren­za di molti altri linguaggi, in Python non è ne­ces­sa­rio uti­liz­za­re un ciclo for per scrivere le lettere di una stringa una per una in un elenco. Si usa invece la funzione list() per con­ver­ti­re un iterabile in un elenco di elementi. Ana­liz­zia­mo entrambi gli approcci con un esempio. Iteriamo le lettere di una parola e le ag­giun­gia­mo a un elenco vuoto:

word = 'Python'
letters = []
for letter in word:
    letters.append(letter)

Possiamo ri­spar­miar­ci la fatica. Creiamo l’elenco di­ret­ta­men­te con la funzione list(). Allo stesso tempo, ve­ri­fi­chia­mo con l’as­ser­zio­ne assert che entrambi i metodi diano lo stesso risultato:

assert list(word) == letters

Un altro esempio. Creiamo un elenco di numeri da zero a nove. Un oggetto range serve come base per un iterabile:

list(range(10))

Oltre agli elenchi, a partire da un iterabile si possono creare anche dei set. Ad esempio, creiamo un set che re­sti­tui­sce le lettere contenute in una frase. Con l’operatore in ve­ri­fi­chia­mo che il set delle lettere non contenga una ‘a’:

alphabet = set('Python is not hyped')
assert 'a' not in alphabet

So­sti­tui­re i cicli for in Python con le com­pren­sio­ni

Un uso comune dei cicli for in Python è quello di mo­di­fi­ca­re gli elementi di un insieme. Potremmo voler calcolare nuovi valori in base a un insieme o filtrare alcuni elementi in base a un modello. Seguendo lo stile di pro­gram­ma­zio­ne im­pe­ra­ti­vo, de­scri­via­mo i singoli passaggi nel modo seguente:

  1. Iterare l’insieme con un ciclo for
  2. Elaborare ogni elemento
  3. Se ne­ces­sa­rio, combinare un sot­toin­sie­me di elementi in un nuovo insieme

Per le modifiche semplici, è piuttosto complesso. I linguaggi fun­zio­na­li di­mo­stra­no che è possibile farlo in modo più semplice. For­tu­na­ta­men­te, Python conosce il concetto di “com­pren­sio­ne” (“com­pre­hen­sion” in inglese). Le com­pren­sio­ni possono so­sti­tui­re semplici ap­pli­ca­zio­ni del ciclo for in Python. Sono più per­for­man­ti di ap­pli­ca­zio­ni equi­va­len­ti di un ciclo for.

Una com­pren­sio­ne crea un insieme mo­di­fi­ca­to basato su un iterabile. Per farlo, viene uti­liz­za­ta una sintassi concisa ed espres­si­va. Vediamo la sintassi generale della com­pren­sio­ne di una lista. Scriviamo l’espres­sio­ne tra parentesi quadre. Viene eseguita un’ope­ra­zio­ne sugli elementi di un insieme; ogni elemento viene copiato in un nuovo elenco:

[ operation(element) for element in collection ]

Inoltre, gli elementi possono essere filtrati in base a de­ter­mi­na­ti modelli. Vengono uti­liz­za­ti un if opzionale e una con­di­zio­ne:

[ operation(element) for element in collection if condition(element) ]

Vediamo ora un esempio di ciclo for in Python che può essere so­sti­tui­to da una com­pren­sio­ne. Abbiamo un elenco di numeri e vogliamo calcolare il cor­ri­spon­den­te elenco di numeri quadrati:

numbers = [2, 3, 5, 9, 17]

Creiamo un elenco vuoto e lo riempiamo con i quadrati all’interno di un ciclo for:

squares = []
for number in numbers:
    squares.append(number ** 2)

L’elenco dei numeri quadrati può essere espresso più sem­pli­ce­men­te sotto forma di com­pren­sio­ne:

squares_comp = [number ** 2 for number in numbers]

Uti­liz­zia­mo quindi l’istru­zio­ne assert per as­si­cu­rar­ci che entrambi i metodi re­sti­tui­sca­no lo stesso risultato:

assert squares == squares_comp

Un altro esempio. De­si­de­ria­mo estrarre le lettere minuscole da una stringa. Creiamo un elenco di lettere sia maiuscole che minuscole da inserire:

word = list("PyThoN")

Il modo con­ven­zio­na­le per estrarre le lettere minuscole è quello di iterare le lettere. Ve­ri­fi­chia­mo ogni lettera con la funzione islower() e la ag­giun­gia­mo a un elenco ini­zial­men­te vuoto se il risultato è positivo:

lowers = []
for letter in word:
    if letter.islower():
        lowers.append(letter)

Possiamo ri­spar­miar­ci questo ciclo for in Python. Uti­liz­zia­mo invece una com­pren­sio­ne che copia solo le lettere minuscole dell’elenco originale:

lowers_comp = [ letter for letter in word if letter.islower() ]

Anche in questo caso, ve­ri­fi­chia­mo l’ugua­glian­za dei due metodi uti­liz­zan­do l’istru­zio­ne assert:

assert lowers == lowers_comp
Vai al menu prin­ci­pa­le