Itérateur, itérable, conteneur, générateur, en Python

par Vincent Poulailleau - 3 minutes de lecture - 507 mots

Je vois régulièrement des confusions entre les termes itérateurs, itérables, conteneurs, générateurs. Voici le schéma à retenir :

Vision globale de l’itération
Vision globale de l’itération

Commençons par la notion d’itérable. Un itérable est quelque chose que l’on peut parcourir avec une boucle for :

1
2
for un_élément in un_itérable:
    faire_quelque_chose_avec(un_élément)

Un conteneur est quelque chose qui contient des valeurs (une liste, un dictionnaire, un set…). Un conteneur est généralement itérable (en tout cas, les listes, dictionnaires et sets le sont).

Les listes en compréhension, les sets en compréhension, les dictionnaires en compréhension sont des moyens de créer des listes, sets et dictionnaires facilement et rapidement.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# mauvaise façon de faire
ma_liste = list()
for numéro in range(10):
    ma_liste.append(numéro * numéro)

# bonne façon de faire, avec une liste en compréhension
ma_liste = [numéro * numéro for numéro in range(10)]

# ou encore
ma_liste = [
    numéro * numéro
    for numéro in range(10)
]

# notation pour les sets
mon_set = {
    numéro * numéro
    for numéro in range(10)
}

# notation pour les dictionnaires
mon_dictionnaire = {
    numéro: numéro * numéro
    for numéro in range(10)
}

Appeler iter sur un itérable renvoie un itérateur :

1
un_itérateur = iter(ma_liste)

Un itérateur est quelque chose qui permet d’itérer sur un itérable, c’est à dire de parcourir séquentiellement l’itérable (une boucle for utilise un itérateur pour parcourir les itérables).

Pour avoir accès au prochain élément fourni par l’itérateur, il faut appeler la fonction next.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ma_liste = [n for n in range(3)]
print("ma_liste :", ma_liste)

mon_itérateur = iter(ma_liste)
print("mon_itérateur :", mon_itérateur)

print(next(mon_itérateur))
print(next(mon_itérateur))
print(next(mon_itérateur))
print(next(mon_itérateur))

Ce qui affichera :

1
2
3
4
5
6
7
8
9
ma_liste : [0, 1, 2]
mon_itérateur : <list_iterator object at 0x7f379b474ac8>
0
1
2
Traceback (most recent call last):
  File "toto.py", line 10, in <module>
    print(next(mon_itérateur))
StopIteration

Comme vous pouvez le voir, quand il n’y a plus d’élément à parcourir, next lève une exception StopIteration.

Vous pouvez créez vos itérateurs et itérables maisons, en créant des classes qui ont les méthodes __iter__ et __next__. Lisez https://docs.python.org/fr/3/reference/datamodel.html qui contient plein d’informations utiles.

Un générateur génère des valeurs à la volée. Un générateur est itérable. Il existe deux types de générateurs : les fonctions génératrices et les expressions génératrices.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# fonction pour illustrer le propos
# ne serait pas écrite comme cela normalement
def fonction_génératrice(maximum):
    entier = 1
    while True:
        if entier <= maximum:
            yield entier
        entier += 1


expression_génératrice = (n for n in range(10, 13))

for n in fonction_génératrice(3):
    print(n)
for n in expression_génératrice:
    print(n)

Cela affichera :

1
2
3
4
5
6
1
2
3
10
11
12

Voilà pour cet article qui ne rentre pas dans les détails. Je vous conseille comme lecture pour les anglophones :