Corso

In questo tutorial parleremo di cos'è la funzione enumerate() in Python, quando si usa, che tipo di oggetti crea, qual è la sua sintassi e come funziona. Poi vedremo alcuni esempi di enumerazione di diversi tipi di oggetti in Python, le alternative alla funzione enumerate() e perché sono meno adatte allo stesso compito.
Cos'è enumerate in Python e quando si usa?
La funzione enumerate() è una funzione built-in di Python che prende un iterabile (una collezione di elementi o qualsiasi altro oggetto Python che supporti l'iterazione, come tuple, liste, set o stringhe), itera sui suoi elementi sotto il cofano e restituisce un oggetto enumerate. In altre parole, questa funzione assegna a ciascun elemento di un iterabile un conteggio che cresce di 1 e ci aiuta a tracciare le iterazioni mentre cicliamo su quell'oggetto. Dopo aver creato un oggetto enumerate, possiamo convertirlo in una lista, una tupla o un dizionario (usando rispettivamente le funzioni list(), tuple() o dict()), oppure ciclare direttamente su questo oggetto per accedere sia agli elementi sia ai loro indici corrispondenti.
Usare la funzione enumerate() è utile dal punto di vista dell'efficienza in termini di memoria e calcolo rispetto ai for-loop, poiché restituisce indice ed elemento corrispondente in un colpo solo.
Sintassi
La sintassi della funzione enumerate() è molto semplice:
enumerate(iterable, start=0)
Qui, iterable è un oggetto iterabile che vogliamo enumerare, e start è un parametro opzionale che definisce l'indice iniziale del contatore (per impostazione predefinita sarà un tipico indice Python basato su 0).
Nota che applicare la funzione enumerate() a un iterabile non modifica l'oggetto iniziale. Il risultato è un oggetto enumerate che, se vogliamo riutilizzare, andrebbe assegnato a una variabile.
Vediamo ora come funziona questa funzione su diversi tipi di iterabili Python.
Enumerare una lista
Per prima cosa, applicheremo la funzione enumerate() a una lista Python:
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
enumerated_drinks = enumerate(drinks)
print(type(enumerated_drinks))
print(enumerated_drinks)
Output:
<class 'enumerate'>
<enumerate object at 0x000002DC3A1145C0>
Il tipo dell'oggetto risultante è enumerate e, quando abbiamo provato a stamparlo, abbiamo semplicemente confermato ancora una volta il tipo dell'oggetto e dove è memorizzato nella memoria del computer. Proviamo ad accedere al primo elemento di questo oggetto nello stesso modo in cui faremmo con una lista o una tupla Python:
print(enumerated_drinks[0])
Output:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_5948/1233607596.py in <module>
----> 1 print(enumerated_drinks[0])
TypeError: 'enumerate' object is not subscriptable
Abbiamo ottenuto un errore che indica che i componenti di un oggetto enumerate non sono accessibili in questo modo. Per farlo, dobbiamo invece convertirlo in una lista, una tupla o un dizionario, oppure ciclare direttamente sull'oggetto enumerate. Convertiamo il nostro oggetto enumerate in una lista:
list_enumerated_drinks = list(enumerated_drinks)
print(list_enumerated_drinks)
Output:
[(0, 'tea'), (1, 'coffee'), (2, 'cappuccino'), (3, 'lemonade')]
Ora vediamo chiaramente la struttura interna della lista risultante: ogni elemento è una tupla a 2 elementi in cui il primo è l'indice dell'elemento corrispondente nella lista originale e il secondo è l'elemento stesso. Possiamo usare il consueto approccio di indicizzazione per accedere agli elementi di questa lista:
print(list_enumerated_drinks[0])
print(list_enumerated_drinks[0][1])
Output:
(0, 'tea')
tea
Proviamo ora a convertire in una tupla un oggetto enumerate creato dalla lista originale. Inoltre, questa volta imposteremo il parametro opzionale start a 1:
tuple_enumerated_drinks = tuple(enumerate(drinks, start=1))
print(tuple_enumerated_drinks)
Output:
((1, 'tea'), (2, 'coffee'), (3, 'cappuccino'), (4, 'lemonade'))
La struttura interna della tupla sopra è identica a quella della lista creata prima. Tuttavia, questa volta abbiamo anche cambiato il conteggio rendendolo più "umano" rispetto all'indicizzazione Python basata su 0.
C'è un metodo interessante di un oggetto enumerate che vale la pena menzionare qui: il metodo next(). Ricreiamo l'oggetto enumerated_drinks e proviamo questo metodo in azione:
enumerated_drinks = enumerate(drinks)
print(next(enumerated_drinks))
print(next(enumerated_drinks))
print(next(enumerated_drinks))
print(next(enumerated_drinks))
print(next(enumerated_drinks))
Output:
(0, 'tea')
(1, 'coffee')
(2, 'cappuccino')
(3, 'lemonade')
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_5948/330134057.py in <module>
4 print(next(enumerated_drinks))
5 print(next(enumerated_drinks))
----> 6 print(next(enumerated_drinks))
StopIteration:
Come vediamo, ogni volta che chiamiamo questo metodo su un oggetto enumerate, esso restituisce l'elemento successivo (e possiamo confermare ancora una volta che ogni elemento è una tupla a 2 elementi contenente la coppia conteggio-elemento corrispondente). Quando gli elementi finiscono, l'oggetto enumerate si esaurisce e il tentativo di chiamare il metodo next() su di esso genera un errore StopIteration. Infatti, se ora proviamo a convertire questo oggetto enumerate in una lista, vedremo che è vuota
print(list(enumerated_drinks))
Output:
[]
Questo significa che a ogni applicazione del metodo next() su un oggetto enumerate viene prelevato un elemento dall'oggetto, quindi ogni volta esso si riduce, fino all'esaurimento totale. Vedremo questa proprietà degli oggetti enumerate anche più avanti quando parleremo di applicarvi i for-loop.
Se vuoi saperne di più sulle liste in Python e su altri concetti di base di Python, puoi seguire il corso Introduction to Python di DataCamp.
Enumerare una tupla
L'applicazione della funzione enumerate() a una tupla è identica a quella su una lista:
drinks = ('tea', 'coffee', 'cappuccino', 'lemonade')
enumerated_drinks = enumerate(drinks)
print(enumerated_drinks)
Output:
<enumerate object at 0x000002DC3A098D40>
Di nuovo, abbiamo ottenuto un oggetto enumerate i cui elementi non sono accessibili con la comune indicizzazione Python. Trasformiamo questo oggetto in un altro, questa volta in un dizionario:
dict_enumerated_drinks = dict(enumerated_drinks)
print(dict_enumerated_drinks)
Output:
{0: 'tea', 1: 'coffee', 2: 'cappuccino', 3: 'lemonade'}
Ogni coppia chiave:valore del dizionario risultante rappresenta la corrispondente coppia conteggio-elemento. Possiamo accedere ai valori del dizionario nel modo Pythonico comune:
print(dict_enumerated_drinks[0])
print(dict_enumerated_drinks[3])
Output:
tea
lemonade
Se vuoi approfondire i dizionari Python e pandas, esplora il corso Intermediate Python.
Enumerare una stringa
Poiché le stringhe Python sono sequenze di caratteri e quindi anche oggetti iterabili, possiamo creare un oggetto enumerate anche per una stringa:
enumerated_hello_world = enumerate('Hello, World!', start=10)
print(enumerated_hello_world)
Output:
<enumerate object at 0x000002DC3A145C40>
Come fatto in precedenza, convertiamo questo oggetto in una lista:
list_enumerated_hello_world = list(enumerated_hello_world)
print(list_enumerated_hello_world)
Output:
[(10, 'H'), (11, 'e'), (12, 'l'), (13, 'l'), (14, 'o'), (15, ','), (16, ' '), (17, 'W'), (18, 'o'), (19, 'r'), (20, 'l'), (21, 'd'), (22, '!')]
Vediamo le ormai familiari tuple a 2 elementi, dove il primo elemento è il conteggio del carattere corrispondente della stringa (in questo caso, a partire da 10) e il secondo è il carattere stesso. Anche lo spazio bianco e la punteggiatura vengono conteggiati.
Ricreiamo l'oggetto enumerated_hello_world, questa volta senza start=10, e applichiamo il metodo next:
enumerated_hello_world = enumerate('Hello, World!')
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
# Creating a list from the remaining enumerate object
print(list(enumerated_hello_world))
Output:
(0, 'H')
(1, 'e')
(2, 'l')
(3, 'l')
(4, 'o')
(5, ',')
[(6, ' '), (7, 'W'), (8, 'o'), (9, 'r'), (10, 'l'), (11, 'd'), (12, '!')]
Nell'ultima riga del codice sopra, abbiamo smesso di applicare il metodo next() e abbiamo convertito l'oggetto enumerated_hello_world in una lista. La lista risultante contiene i caratteri rimanenti dell'oggetto enumerate iniziale dopo le molteplici applicazioni del metodo next() su di esso.
Enumerare con un for-loop
Infine, vediamo come funziona la funzione enumerate() in combinazione con un for-loop:
for item in enumerate(['tea', 'coffee', 'cappuccino', 'lemonade']):
print(item)
Output:
(0, 'tea')
(1, 'coffee')
(2, 'cappuccino')
(3, 'lemonade')
A ogni iterazione viene restituita la corrispondente tupla conteggio-elemento, proprio come accade quando usiamo il metodo next(). Anche se non è ovvio dal codice sopra, l'oggetto enumerate perde anch'esso i suoi elementi (cioè le tuple a 2 elementi) uno alla volta a ogni iterazione. Per dimostrarlo, riscriviamo il codice sopra in un'altra forma e controlliamo la lunghezza della lista risultante dopo aver terminato tutte le iterazioni:
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
enumerated_drinks = enumerate(drinks)
for item in enumerated_drinks:
print(item)
print(list(enumerated_drinks))
Output:
(0, 'tea')
(1, 'coffee')
(2, 'cappuccino')
(3, 'lemonade')
[]
Come con l'applicazione del metodo next(), iterare su un oggetto enumerate lo esaurisce gradualmente.
Tuttavia, torniamo al pezzo di codice precedente. Come possiamo accedere separatamente a ciascun elemento di ogni tupla a 2 elementi? A questo scopo, aggiungeremo una variabile di iterazione e decomprimeremo le tuple:
for count, drink in enumerate(['tea', 'coffee', 'cappuccino', 'lemonade']):
print(count, drink)
Output:
0 tea
1 coffee
2 cappuccino
3 lemonade
Qui sopra abbiamo restituito due variabili per ogni tupla: una per il conteggio dell'elemento e una per l'elemento stesso.
Nota che possiamo implementare la stessa operazione di sopra anche senza usare la funzione enumerate(). Per esempio, possiamo introdurre una variabile contatore fuori dal ciclo e incrementarla di 1 a ogni iterazione del ciclo:
count = 0
for drink in ['tea', 'coffee', 'cappuccino', 'lemonade']:
print(count, drink)
count += 1
Output:
0 tea
1 coffee
2 cappuccino
3 lemonade
In alternativa, possiamo usare una combinazione delle funzioni range() e len() su una lista predefinita (o una tupla, una stringa, ecc.):
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
for count in range(len(drinks)):
print(count, drinks[count])
Output:
0 tea
1 coffee
2 cappuccino
3 lemonade
Inoltre, in entrambe queste alternative alla funzione enumerate(), possiamo comunque impostare la variabile conteggio per iniziare non da zero:
count = 1
for drink in ['tea', 'coffee', 'cappuccino', 'lemonade']:
print(count, drink)
count += 1
print('\n')
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
for count in range(len(drinks)):
print(count + 1, drinks[count])
Output:
1 tea
2 coffee
3 cappuccino
4 lemonade
1 tea
2 coffee
3 cappuccino
4 lemonade
Tuttavia, rispetto all'uso della funzione enumerate(), entrambi gli approcci sopra sono più macchinosi, soggetti a errori (ad esempio, possiamo dimenticare di aggiornare manualmente il contatore a ogni iterazione) e richiedono più codice. Quindi, usare la funzione enumerate() è l'approccio più Pythonico ogni volta che abbiamo bisogno di accedere sia al conteggio sia all'elemento di un iterabile a ogni iterazione.
Conclusione
In questo tutorial abbiamo imparato molte cose sulla funzione enumerate() in Python, inclusi i suoi numerosi usi, le potenziali alternative e perché alla fine risulta più efficace di queste ultime. La funzione enumerate() potrebbe non essere familiare alla maggior parte di chi inizia con Python e persino programmatori esperti potrebbero non conoscere questa funzione incredibilmente utile.
Se vuoi approfondire enumerate(), valuta il corso Python Data Science Toolbox (Part 2), che esplora in dettaglio iteratori e iterabili e offre esercizi interattivi per mettere in pratica le nuove competenze. Inoltre, se vuoi padroneggiare altri concetti di Python, potrebbe interessarti Learn Python with DataCamp, che offre vari percorsi di competenze e di carriera, cheat sheet, tutorial, certificazioni Python e test di valutazione.

