Nella programmazione Python, la comprensione e l’utilizzo efficace degli iterabili è fondamentale per creare codice efficace. Gli iterabili sono oggetti attraverso i quali è possibile iterare o creare un loop. Supportano lo scorrimento sequenziale degli elementi al loro interno, il che li rende uno strumento fondamentale per accedere e manipolare elementi in oggetti o strutture di dati.

Questo articolo spiega come utilizzare correttamente gli iterabili in Python, concentrandosi sui tipi di dati iterabili integrati nel linguaggio: liste, tuple, dizionari, stringhe e set. Inoltre, spiega come implementare tipi di iterabili personalizzati ed eseguire operazioni avanzate.

Come eseguire il loop degli iterabili in Python

In Python, è possibile iterare attraverso diversi tipi di iterabili utilizzando un ciclo for. Ciò permette di navigare in sequenze e di eseguire operazioni su singoli elementi all’interno di liste, set e dizionari.

La parola chiave for in Python si discosta dalla sua utilità in altri linguaggi orientati agli oggetti come Java. I loop di Python for funzionano più come i metodi degli iteratori. Ecco alcuni esempi che dimostrano i loop negli iteratori:

1. Loop in una lista

Gli elenchi sono raccolte ordinate di elementi che consentono una facile iterazione con un ciclo for.

fruits_list = ["Apple", "Mango", "Peach", "Orange", "Banana"]

for fruit in fruits_list:
    print(fruit)

Nel codice qui sopra, fruit agisce come un iteratore che il ciclo utilizza per scorrere ogni elemento dell’elenco e contemporaneamente stamparlo. Il ciclo termina dopo aver valutato l’ultimo elemento dell’elenco. Il codice precedente dovrebbe dare il seguente risultato:

Apple
Mango
Peach
Orange
Banana

2. Iterare una tupla

Le tuple sono simili alle liste ma sono immutabili. Si possono iterare proprio come le liste.

fruits_tuple = ("Apple", "Mango", "Peach", "Orange", "Banana")

for fruit in fruits_tuple:
	print(fruit)

In questo esempio, il ciclo for itera attraverso la tupla e ad ogni iterazione la variabile fruit assume il valore dell’elemento corrente della tupla. Il codice dovrebbe dare il seguente risultato:

Apple
Mango
Peach
Orange
Banana

3. Looping attraverso i set

I set sono raccolte non ordinate di elementi unici. Si possono scorrere utilizzando un ciclo for.

fruits_set = {"Apple", "Mango", "Peach", "Orange", "Banana"}

for fruit in fruits_set:
	print(fruit)

In questo esempio, il ciclo for itera attraverso il set. Tuttavia, poiché gli insiemi non sono ordinati, l’ordine di iterazione potrebbe non corrispondere all’ordine in cui gli elementi sono stati definiti nel set. In ogni iterazione, la variabile fruit assume il valore dell’elemento corrente dell’insieme. Il codice dovrebbe dare un risultato simile al seguente (l’ordine può variare):

Mango
Banana
Peach
Apple
Orange

4. Iterazione di stringhe

Le stringhe sono sequenze di caratteri che si possono scorrere carattere per carattere.

string = "Kinsta"

for char in string:
	print(char)

Il codice qui sopra itera la stringa “Kinsta” e stampa ogni carattere su una nuova riga. In ogni iterazione, la variabile char assume il valore del carattere corrente della stringa. Il codice dovrebbe dare il seguente risultato:

K
i
n
s
t
a

5. Scorrimento di un dizionario

L’utilizzo del ciclo for è simile per liste, set, tuple e stringhe, ma è diverso per i dizionari, che utilizzano coppie chiave-valore per memorizzare gli elementi. I dizionari rappresentano un caso unico per l’esecuzione del ciclo, in quanto è possibile iterarli utilizzando approcci diversi. Ecco i diversi approcci che si possono utilizzare per scorrere un dizionario in Python:

  • Iterazione attraverso le chiavi:
    countries_capital = {
        "USA": "Washington D.C.",
        "Australia": "Canberra",
        "France": "Paris",
        "Egypt": "Cairo",
        "Japan": "Tokyo"
    }
    
    for country in countries_capital.keys():
        print(country)

    Il codice precedente definisce un dizionario chiamato countries_capital, dove i nomi dei paesi sono le chiavi e le rispettive capitali sono i valori. Il ciclo for esegue l’iterazione delle chiavi del dizionario countries_capital utilizzando il metodo keys(). Questo metodo restituisce un oggetto vista che visualizza un elenco delle chiavi del dizionario, il che facilita l’esecuzione del ciclo su tutte le chiavi. Ad ogni iterazione, la variabile country assume il valore della chiave corrente. Questo codice dovrebbe dare il seguente risultato:

    USA
    Australia
    France
    Egypt
    Japan
  • Iterazione dei valori:
    countries_capital = {
        "USA": "Washington D.C.",
        "Australia": "Canberra",
        "France": "Paris",
        "Egypt": "Cairo",
        "Japan": "Tokyo"
    }
    
    for capital in countries_capital.values():
        print(capital)

    Nel codice qui sopra, for itera i valori del dizionario countries_capital utilizzando il metodo values(). Questo metodo restituisce un oggetto vista che visualizza un elenco dei valori del dizionario, rendendo più semplice l’iterazione di tutti i valori. Ad ogni iterazione, la variabile capital assume il valore del valore corrente nell’elenco. Questo codice dovrebbe dare il seguente risultato:

    Washington D.C.
    Canberra
    Paris
    Cairo
    Tokyo
  • Iterazione di coppie chiave-valore:
    countries_capital = {
        "USA": "Washington D.C.",
        "Australia": "Canberra",
        "France": "Paris",
        "Egypt": "Cairo",
        "Japan": "Tokyo"
    }
    
    for country, capital in countries_capital.items():
        print(country, ":", capital)

    Il codice qui sopra mostra come iterare sia le chiavi che i valori del dizionario countries_capital utilizzando il metodo items(). Il metodo items() restituisce un oggetto vista che visualizza un elenco di tuple chiave-valore del dizionario. Nel ciclo for, ogni iterazione scompone una coppia chiave-valore dall’elemento corrente dell’elenco. Alle variabili country e capital vengono assegnati rispettivamente la chiave e il valore corrispondenti. Questo codice dovrebbe dare il seguente risultato:

    USA : Washington D.C.
    Australia : Canberra
    France : Paris
    Egypt : Cairo
    Japan : Tokyo

Iterazione avanzata con enumerate() in Python

Un altro modo per iterare su iterabili in Python restituendo sia l’indice che il valore corrispondente degli elementi è la funzione enumerate(). Diamo uno sguardo a questo esempio:

fruits_list = ["Apple", "Mango", "Peach", "Orange", "Banana"]

for index, fruit in enumerate(fruits_list):
    print(fruit, index)

Ecco l’output:

Apple 0
Mango 1
Peach 2
Orange 3
Banana 4

La funzione enumerate permette anche di specificare l’indice iniziale, oltre a 0, per l’operazione di iterazione. Possiamo modificare l’esempio precedente come segue:

fruits_list = ["Apple", "Mango", "Peach", "Orange", "Banana"]
for index, fruit in enumerate(fruits_list, start = 2):
    print(fruit, index)

Ecco l’output:

Apple 2
Mango 3
Peach 4
Orange 5
Banana 6

Notiamo che, sebbene questo esempio specifichi l’indice iniziale dell’enumerazione, enumerate non applica un’indicizzazione a base zero all’iterabile, come avviene con le liste native. Applica semplicemente il valore iniziale al primo elemento dell’elenco fino all’ultimo.

Come implementare i generatori Python

I generatori sono speciali iterabili Python che permettono di costruire oggetti generatori senza creare esplicitamente tipi incorporati come liste, set o dizionari. Possiamo usare i generatori per produrre valori man mano in base alla logica di generazione.

I generatori utilizzano l’istruzione yield per restituire i valori generati uno alla volta. Ecco un esempio di generatori iterabili:

def even_generator(n):
    counter = 0
    while counter <= n:
        if counter % 2 == 0:
            yield counter
        counter += 1

for num in even_generator(20):
    print(num)

Il codice fornito definisce una funzione even_generator che produce una sequenza di numeri pari da 0 a un n specificato utilizzando l’istruzione yield. Utilizza un ciclo per generare questi valori e itera il risultato utilizzando l’iteratore num, assicurando la valutazione di tutti i valori all’interno dell’intervallo indicato. Questo codice produce un elenco di numeri pari da 0 a 20, come mostrato di seguito:

0
2
4
6
8
10
12
14
16
18
20

Possiamo ottenere una maggiore concisione quando lavoriamo con le espressioni del generatore. Ad esempio, possiamo progettare una funzione generatore che incorpori anche la logica del ciclo:

cube = (num ** 3 for num in range(1, 6))
for c in cube:
    print(c)

In questo caso, assegniamo la variabile cube al risultato di una funzione che calcola il cubo dei valori compresi nell’intervallo da 1 a 6. La funzione è in grado di generare un ciclo di valori all’interno dell’intervallo specificato. La funzione esegue poi un ciclo di valori all’interno dell’intervallo specificato, producendo i risultati del calcolo uno dopo l’altro. L’output è il seguente:

1
8
27
64
125

Come costruire iterabili personalizzati

Python permette di personalizzare ulteriormente le operazioni iterabili utilizzando gli iteratori. Gli oggetti iteratore implementano il protocollo iteratore e contengono due metodi: __iter__() e __next__(). Il metodo __iter__() restituisce un oggetto iteratore, mentre __next__() restituisce il valore successivo in un contenitore iterabile. Ecco un esempio di iteratori in Python:

even_list = [2, 4, 6, 8, 10]
my_iterator = iter(even_list)
print(next(my_iterator)) # Prints 2
print(next(my_iterator)) # Prints 4
print(next(my_iterator)) # Prints 6

In questo esempio, utilizziamo il metodo iter() per creare un oggetto iteratore (my_iterator) dall’elenco. Per accedere a ciascun elemento dell’elenco, avvolgiamo l’oggetto iteratore con il metodo next(). Poiché gli elenchi sono collezioni ordinate, l’iteratore restituisce gli elementi in sequenza.

Gli iteratori personalizzati sono ideali per le operazioni che coinvolgono grandi insiemi di dati che non si possono caricare in memoria contemporaneamente. Poiché la memoria è costosa e soggetta a vincoli di spazio, possiamo usare un iteratore per elaborare gli elementi dei dati singolarmente senza caricare l’intero set di dati in memoria.

Funzioni iterabili

Python utilizza delle funzioni per spostarsi, manipolare e ispezionare gli elementi di una lista. Alcune funzioni comuni per gli elenchi sono:

  • sum – restituisce la somma di un dato iterabile, a condizione che l’insieme sia di tipo numerico (numeri interi, valori in virgola mobile e numeri complessi)
  • any – restituisce true se uno qualsiasi degli elementi dell’iterabile è vero. Altrimenti, restituisce false.
  • all – restituisce true se tutti gli elementi dell’iterabile sono veri. Altrimenti, restituisce false.
  • max – restituisce il valore massimo di un dato insieme iterabile
  • min – restituisce il valore minimo di un dato insieme iterabile
  • len – restituisce la lunghezza di un dato iterabile
  • append – aggiunge un valore alla fine di un elenco iterabile

L’esempio seguente mostra queste funzioni con un elenco:

even_list = [2, 4, 6, 8, 10]

print(sum(even_list))
print(any(even_list))
print(max(even_list))
print(min(even_list))
even_list.append(14) # Add 14 to the end of the list
print(even_list)
even_list.insert(0, 0) # Insert 0 and specified index [0]
print(even_list)
print(all(even_list)) # Return true only if ALL elements in the list are true
print(len(even_list)) # Print the size of the list

Ecco l’output:

30
True
10
2
[2, 4, 6, 8, 10, 14]
[0, 2, 4, 6, 8, 10, 14]
False
7

Nell’esempio precedente, la funzione append aggiunge un singolo parametro (14) alla fine dell’elenco. La funzione insert permette di specificare l’indice di inserimento. Pertanto, even_list.insert(0, 0) inserisce 0 all’indice [0].
L’istruzione print(all(even_list)) restituisce false perché nell’elenco è presente un valore 0, interpretato come false. Infine, print(len(even_list)) restituisce la lunghezza dell’iterabile.

Operazioni iterabili avanzate

Python offre funzioni avanzate che favoriscono la concisione delle operazioni iterabili. Di seguito ne elenchiamo alcune.

1. Comprensioni di lista

Le comprensioni di lista permettono di creare nuovi elenchi applicando una funzione a ciascun elemento degli elenchi esistenti. Ecco un esempio:

my_numbers = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
even_number_list = [num for num in my_numbers if num%2 == 0]
print(even_number_list)

In questo frammento di codice, viene creata una lista chiamata my_numbers con i numeri interi da 11 a 20. L’obiettivo è quello di generare una nuova lista, even_number_list, contenente solo numeri interi pari. Per raggiungere questo obiettivo, si applica una comprensione della lista che restituisce un intero da my_numbers solo se quell’intero è pari. L’istruzione if contiene la logica che restituisce i numeri pari.

Ecco l’output:

[12, 14, 16, 18, 20]

2. Zip

La funzione zip() di Python combina più iterabili in tuple. Le tuple memorizzano più valori in un’unica variabile e sono immutabili. Ecco come combinare gli iterabili usando zip():

fruits = ["apple", "orange", "banana"]
rating = [1, 2, 3]

fruits_rating = zip(rating, fruits)
print(list(fruits_rating))

In questo esempio, fruits_rating accoppia ogni valutazione con un frutto, creando un unico iterabile. L’output è:

[(1, 'apple'), (2, 'orange'), (3, 'banana')]

Questo codice agisce come un sistema di valutazione per diversi frutti, con il primo elenco (fruits) che rappresenta i frutti e il secondo elenco che rappresenta le valutazioni su una scala da 1 a 3.

3. Filter

Un’altra funzione avanzata, filter, accetta due argomenti: una funzione e un iterabile. Applica la funzione a ogni elemento dell’iterabile e restituisce un nuovo iterabile contenente solo gli elementi per i quali la funzione restituisce un valore true. L’esempio seguente filtra un elenco di valori interi all’interno di un determinato intervallo per restituire solo quelli pari:

def is_even(n):
    return n%2 == 0

nums_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_list = filter(is_even, nums_list)
print(list(even_list))

Nel codice qui sopra, iniziamo definendo una funzione, is_even, per calcolare un numero pari passato a tale funzione. Poi, creiamo un elenco di valori interi compresi tra 1 e 10 – nums_list. Infine, definiamo una nuova lista, even_list, che utilizza la funzione filter() per applicare il metodo definito dall’utente alla lista originale e restituire solo gli elementi della lista pari. Ecco il risultato:

[2, 4, 6, 8, 10]

4. Map

Come filter(), la funzione map() di Python prende come argomenti un’iterabile e una funzione. Ma invece di restituire gli elementi dell’iterabile iniziale, restituisce una nuova iterabile contenente il risultato della funzione applicata a ciascun elemento della prima iterabile. Per elevare al quadrato un elenco di numeri interi, usiamo la funzione map():

my_list = [2, 4, 6, 8, 10]
square_numbers = map(lambda x: x ** 2, my_list)
print(list(square_numbers))

In questo codice, x è l’iteratore che attraversa l’elenco e lo trasforma attraverso il calcolo del quadrato. La funzione map() esegue questa operazione prendendo come argomento l’elenco originale e una funzione di mappatura. L’output è il seguente:

[4, 16, 36, 64, 100]

5. Sorted

La funzione sorted ordina gli elementi di un dato iterabile in un ordine specifico (ascendente o discendente) e lo restituisce come elenco. Accetta un massimo di 3 parametri: iterable, reverse(opzionale) e key(opzionale). reverse ha come valore predefinito False e, se impostato su True, gli elementi vengono ordinati in ordine decrescente. key è una funzione che calcola un valore per determinare l’ordine degli elementi di un iterabile e ha come valore predefinito None.

Ecco un esempio di come possiamo applicare la funzione sorted a diversi iterabili:

# set
py_set = {'e', 'a', 'u', 'o', 'i'}
print(sorted(py_set, reverse=True))

# dictionary
py_dict = {'e': 1, 'a': 2, 'u': 3, 'o': 4, 'i': 5}
print(sorted(py_dict, reverse=True))

# frozen set
frozen_set = frozenset(('e', 'a', 'u', 'o', 'i'))
print(sorted(frozen_set, reverse=True))

# string
string = "kinsta"
print(sorted(string))

# list
py_list = ['a', 'e', 'i', 'o', 'u']
print(py_list)

Si ottiene il seguente risultato:

['u', 'o', 'i', 'e', 'a']
['u', 'o', 'i', 'e', 'a']
['u', 'o', 'i', 'e', 'a']
['a', 'i', 'k', 'n', 's', 't']
['a', 'e', 'i', 'o', 'u']

Come gestire i casi limite e gli errori negli iterabili

I casi limite sono comuni in molti scenari di programmazione e devono essere previsti negli iterabili. Esploriamo alcune possibilità che potremmo incontrare.

1. Iterabili vuoti

Potremmo incontrare dei problemi quando un iterabile è vuoto, ma una logica di programmazione tenta di scorrerlo. Possiamo risolvere questo problema in modo programmatico per evitare inefficienze. Ecco un esempio che utilizza un’istruzione if not per verificare se un elenco è vuoto:

fruits_list=[]
if not fruits_list:
    print("The list is empty")

Questo è il risultato:

The list is empty

2. Iterabili annidati

Python supporta anche gli iterabili annidati, ovvero oggetti iterabili che contengono altri iterabili al loro interno. Ad esempio, possiamo avere un elenco di alimenti che contiene elenchi annidati di categorie di alimenti, come carne, verdure e cereali. Ecco come modellare uno scenario del genere utilizzando gli iterabili annidati:

food_list = [["kale", "broccoli", "ginger"], ["beef", "chicken", "tuna"], ["barley", "oats", "corn"]]
for inner_list in food_list:
    for food in inner_list:
        print(food)

Nel codice qui sopra, la variabile food_list contiene tre elenchi annidati, che rappresentano diverse categorie di alimenti. Il ciclo esterno (for inner_list in food_list:) itera attraverso l’elenco primario e il ciclo interno (for food in inner_list:) itera attraverso ogni elenco annidato, stampando ogni alimento. L’output è il seguente:

kale
broccoli
ginger
beef
chicken
tuna
barley
oats
corn

3. Gestione delle eccezioni

In Python, gli iterabili supportano anche operazioni di gestione delle eccezioni. Ad esempio, potremmo iterare su una lista e incontrare un errore IndexError. Questo errore significa che stiamo cercando di fare riferimento a un elemento che supera i limiti dell’iterabile. Ecco come gestire un’eccezione di questo tipo utilizzando un blocco try-except:

fruits_list = ["apple", "mango", "orange", "pear"]
try:
    print(fruits_list[5])
except IndexError:
    print("The index specified is out of range.")

Nel codice qui sopra, l’iterabile fruits_list contiene cinque elementi mappati dagli indici 0 e 5 nella collezione di liste. La frase try contiene una funzione print che tenta di visualizzare il valore all’indice 5 dell’iterabile, che non esiste. Viene eseguita la funzione except clause, returning the associated error message. The console returns the error:

The index specified is out of range.

Riepilogo

Imparare a usare al meglio l’iterazione in Python è fondamentale per ottenere un codice efficiente e leggibile. Comprendere i vari modi per iterare su diverse strutture di dati, utilizzare le comprensioni, i generatori e sfruttare le funzioni integrate vi renderà dei programmatori Python esperti.

Sia che si lavori con liste, dizionari, stringhe o oggetti personalizzati, sapere come usare e manipolare gli iterabili è un’abilità indispensabile nella programmazione Python.

Quando avrete finito di costruire la vostra applicazione Python e vorrete ospitarla online, provate l’Hosting di Applicazioni di Kinsta. I primi 20 dollari ve li offriamo noi!

Ci siamo persi qualcosa in questa guida? Condividetelo nella sezione commenti qui sotto!

Jeremy Holcombe Kinsta

Content & Marketing Editor presso Kinsta, web developer di WordPress e content writer. Al di fuori di tutto ciò che riguarda WordPress, mi piacciono la spiaggia, il golf e il cinema. Ho anche i problemi di tutte le persone più alte della media ;).