Contexte

Python est un langage de programmation très abordable que l’on démarre en programmation ou que l’on connaisse déjà d’autres langages. Néanmoins, ce n’est pas parce qu’un langage est abordable qu’il n’a pas ses propres idiomes et qu’il n’y a pas des trucs & astuces à connaître et à retenir.

Ci-après, une petite note d’apprentissage de Python, sur l’utilisation de enumerate.

NB : c’est du Python 3 qui est utilisé.

Si les éléments de ma liste étaient comptés

En Python, le for n’est pas comme le for du langage C dans lequel pour itérer sur un tableau, on fait varier la valeur de l’indice de ce tableau. En Python, le for permet d’itérer sur chacun des éléments de la séquence : la variable de boucle va successivement prendre la valeur de chacun des éléments du tableau.

Voyons un exemple simple avec une liste de nombres :

1
2
3
4
5
6
7
8
9
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']

print('letters {}'.format(letters))

print('\n##########\n')

print('In Python, the for is a foreach : ')
for letter in letters:
    print(letter)

Cela donne comme résultat si on exécute ce script :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
letters ['a', 'b', 'c', 'd', 'e', 'f', 'g']

##########

In Python, the for is a foreach :
a
b
c
d
e
f
g

La variable de boucle a bien pris successivement la valeur de chacun des éléments de la liste.

Cependant, comment fait-on si on souhaite avoir également l’indice de l’élément de la liste ?

Une manière naïve de le faire est d’utiliser range, en créant un intervalle de 0 à la taille de la liste, comme dans l’exemple ci-après.

1
2
3
print('Naive approach to access both index of element in the list and the associated value : ')
for index in range(len(letters)):
    print('letters[{}] : {}'.format(index, letters[index]))

Cela donne comme résultat :

1
2
3
4
5
6
7
8
Naive approach to access both index of element in the list and the associated value :
letters[0] : a
letters[1] : b
letters[2] : c
letters[3] : d
letters[4] : e
letters[5] : f
letters[6] : g

Cela fonctionne mais cela ne semble pas très élégant. Python propose une solution naive pour avoir l’indice et la valeur, en utilisant enumerate (vous ne l’aviez pas vu venir ? ;-) ). Voici sans plus tarder un exemple :

1
2
3
print('using enumerate in a for loop : ')
for index, value in enumerate(letters):
    print('letters[{}] : {}'.format(index, value))

Ce qui donne :

1
2
3
4
5
6
7
8
using enumerate in a for loop :
letters[0] : a
letters[1] : b
letters[2] : c
letters[3] : d
letters[4] : e
letters[5] : f
letters[6] : g

enumerate énumère pour chacun des éléments de la liste en paramètre, son indice et la valeur associée.

Si cela fonctionne avec un for, cela fonctionne aussi avec une compréhension :

1
2
3
print('using enumerate in a for comprehension : ')
list_to_print = ['letters[{}] : {}'.format(index, value) for index, value in enumerate(letters)]
print('\n'.join(list_to_print))

Ce qui donne :

1
2
3
4
5
6
7
8
using enumerate in a for comprehension :
letters[0] : a
letters[1] : b
letters[2] : c
letters[3] : d
letters[4] : e
letters[5] : f
letters[6] : g

Et si on n’a besoin que de l’indice ? Pas de soucis, il suffit d’ignorer la valeur en utilisant classiquement le underscore _ comme nom de variable pour la valeur que l’on souhaite ignorer :

1
2
3
print('using enumerate in a for loop and ignoring the value : ')
for index, _ in enumerate(letters):
    print('index of letter :', index)

Ce qui donne :

1
2
3
4
5
6
7
8
using enumerate in a for loop and ignoring the value :
index of letter : 0
index of letter : 1
index of letter : 2
index of letter : 3
index of letter : 4
index of letter : 5
index of letter : 6

On peut utiliser enumerate indépendamment d’une boucle for directement pour créer une liste de tuples (indice, valeur) à partir d’une liste de valeurs :

1
2
3
print('Creating a list of tuples (index, value) from a list of values')
letters_with_index = list(enumerate(letters))
print(letters_with_index)

Ce qui donne :

1
2
Creating a list of tuples (index, value) from a list of values
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e'), (5, 'f'), (6, 'g')]

Enfin enumerate peut s’utiliser avec d’autres itérables comme les tuples, les chaines de caractères ou les ensembles :

1
2
3
4
5
6
7
# enumerate works with tuple, string and set for example
print('enumerate with tuple : ')
print(list(enumerate(('a', 'e', 'i', 'o', 'u'))))
print('enumerate with string : ')
print(list(enumerate(('aeiou'))))
print('enumerate with set : ')
print(list(enumerate({'a', 'e', 'i', 'o', 'u', 'a', 'e'})))

Ce qui donne :

1
2
3
4
5
6
enumerate with tuple :
[(0, 'a'), (1, 'e'), (2, 'i'), (3, 'o'), (4, 'u')]
enumerate with string :
[(0, 'a'), (1, 'e'), (2, 'i'), (3, 'o'), (4, 'u')]
enumerate with set :
[(0, 'o'), (1, 'i'), (2, 'u'), (3, 'e'), (4, 'a')]

Vous noterez que pour le set, la liste de tuples ne correspond pas à l’ordre de saisie, ce qui est normal puisque les set sont non-ordonnés en Python.

En bref

Quand vous avez besoin d’avoir l’indice associé à l’élément d’une séquence, pensez à utiliser enumerate!

Un gist avec tous les exemples ci-dessus regroupés.

Laissons le mot de la fin à Boromir :

One does not simply range over the len of a sequence in a for loop, one uses enumerate in Python