Come accedere all'indice in loop 'Foreach' in Python

Spesso quando si sta facendo un loop su una collezione di articoli in Python è necessario avere anche un numero ordinale associato ad ogni articolo. Tale numero è solitamente indicato come indice.

Il modo pitonico di accedere all’indice mentre si fa un foreach-loop è quello di usare la funzione integrata enumerate().

enumerate() aggiunge un indice ad ogni elemento di una collezione iterabile. Si può quindi usare la funzione list() builtin per ottenere una lista di tuple.

>>> names = ['Bob', 'Alice', 'John', 'Cindy']
>>> list(enumerate(names))
[(0, 'Bob'), (1, 'Alice'), (2, 'John'), (3, 'Cindy')]

Nota, l’indicizzazione Python inizia da 0.

Tuttavia, se si vuole iniziare l’indicizzazione da 1 (per esempio, quando si stampano i dati di un report), si ottiene banalmente passando un ulteriore argomento start a enumerate.

>>> names = ['Bob', 'Alice', 'John', 'Cindy']
>>> list(enumerate(names), start=1)
[(1, 'Bob'), (2, 'Alice'), (3, 'John'), (4, 'Cindy')]

L’approccio di cui sopra è utile anche se si stampano dati da diverse fonti di dati ma si vuole avere un indice coerente tra tutti i dati.

>>> data_source1 = ['Bob', 'Alice', 'John', 'Cindy']
>>> data_source2 = ['Sarah', 'Jeff', 'Emily', 'Adam']
>>> list(enumerate(data_source1, start=1))
[(1, 'Bob'), (2, 'Alice'), (3, 'John'), (4, 'Cindy')]
>>> list(enumerate(data_source2, start=len(data_source1) + 1))
[(5, 'Sarah'), (6, 'Jeff'), (7, 'Emily'), (8, 'Adam')]

Si noti che non è necessario convertire un oggetto Enumerate in una lista. È esso stesso un iterabile, quindi può iterare direttamente su di esso.

>>> names = ['Bob', 'Alice', 'John', 'Cindy']
>>> for idx, name in enumerate(names):
...   print(name)
...
Bob
Alice
John
Cindy

Ci possono essere altri modi per ottenere un indice mentre si itera su un insieme di elementi, ma l’uso di enumerate() è considerato canonico ed è preferito.

Un’altra opzione sarebbe quella di usare una variabile indice separata, inizializzarla manualmente e incrementarlo manualmente ad ogni iterazione. Un tale approccio, tuttavia, sarebbe considerato una reinvenzione della ruota e disapprovato.

Ecco una citazione da PEP 279 in cui è stata introdotta la funzione enumerate().

Questo PEP introduce una nuova funzione incorporata, enumerate() per semplificare un idioma di looping comunemente usato. Fornisce a tutte le raccolte iteritems() lo stesso vantaggio che gli iteritems() offrono ai dizionari – ** una notazione di indice compatta, leggibile, affidabile**.

Ci si può chiedere se ci sono implicazioni di performance nell’uso di enumerate nel codice. Bene, siete liberi di fare tutte le misurazioni, ma tenete presente, per favore, che enumerare è un modo idiomatico per accedere ad un indice di un iterabile in un ciclo for-each. Il codice idiomatico è raccomandato dai manutentori di Python e faranno tutto per farla funzionare in modo efficiente.

E per quelli di voi che sono più curiosi di seguito è una possibile implementazione della funzione enumerate() dal PEP di cui sopra.

def enumerate(collection):
   'Generates an indexed series:  (0,coll[0]), (1,coll[1]) ...'
   i = 0
   it = iter(collection)
   while 1:
       yield (i, it.next())
       i += 1

Articolo correlato - Python Loop

  • Una linea for ciclo in Python