Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

boucle for

rappel pour itérer sur une liste ou un ensemble :

for item in container:
   ...

sur un dictionnaire

for cle, valeur in d.items():
    ...

en fait on peut itérer sur un grand nombre d’objets
que par définition on appelle itérables
dont une sous-famille remarquable est celle des itérateurs

itérateurs

vous voulez faire une boucle for sur les entiers de 0 à 106
avec ce qu’on a vu jusqu’ici, on serait tenté de

  1. construire une liste avec les entiers de 0 à 1 million

  2. et itérer sur cette liste avec un for

MAIS ce n’est pas une bonne idée
notamment car ça demande une grosse allocation mémoire
qui est loin d’être gratuite en termes de performance
en fait pour faire cette énumération
on n’a besoin que d’une seule case mémoire

// en C++ on écrirait quelque chose comme
for (int i=0; i<=1000000; i++) {
    ...
}

range()

Python offre pour cela une fonction prédéfinie range(n)

0
1
2
3

le résultat de la fonction range() n’est pas une liste
c’est ce qu’on appelle un itérateur
c’est à dire un tout petit objet (en terme d’occupation mémoire)
qui ne fait que mémoriser où on en est dans l’itération

range() - suite

l’impact sur les performances est majeur !

291 ns ± 6.89 ns per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
48
40.6 ms ± 7.24 ms per loop (mean ± std. dev. of 7 runs, 5 loops each)
8000056

enumerate()

quand on itère sur une
liste, comment faire quand
à l’intérieur de la boucle
on a besoin de l’indice
d’énumération ?

0: james
1: henri
2: louis
0: james
1: henri
2: louis

c’est le propos de la fonction enumerate()

ce style de programmation à base d’indices est tentant, surtout lorsqu’on vient d’un autre langage, mais c’est considéré comme pas du tout pythonique. Il est important de prendre l’habitude d’utiliser la boucle for de la bonne façon, notamment car la programmation par indice ne se prête pas du tout aux itérations plus sophistiquées que nous allons étudier.

boucle for - généralisation

de manière générale, on peut écrire une boucle for sur un très grand nombre d’objets :

V de ♢
V de ♧
V de ♡
V de ♤
D de ♢
D de ♧
D de ♡
D de ♤
R de ♢
R de ♧
R de ♡
R de ♤
As de ♢
As de ♧
As de ♡
As de ♤
('♢', '♧', '♡', '♤')
('♢', '♧', '♤', '♡')
('♢', '♡', '♧', '♤')
('♢', '♡', '♤', '♧')
('♢', '♤', '♧', '♡')
('♢', '♤', '♡', '♧')
('♧', '♢', '♡', '♤')
('♧', '♢', '♤', '♡')
('♧', '♡', '♢', '♤')
('♧', '♡', '♤', '♢')
('♧', '♤', '♢', '♡')
('♧', '♤', '♡', '♢')
('♡', '♢', '♧', '♤')
('♡', '♢', '♤', '♧')
('♡', '♧', '♢', '♤')
('♡', '♧', '♤', '♢')
('♡', '♤', '♢', '♧')
('♡', '♤', '♧', '♢')
('♤', '♢', '♧', '♡')
('♤', '♢', '♡', '♧')
('♤', '♧', '♢', '♡')
('♤', '♧', '♡', '♢')
('♤', '♡', '♢', '♧')
('♤', '♡', '♧', '♢')

Dans le cas de la fonction itertools.product, remarquez que notre code revient à faire deux boucles imbriquées.

break et continue

dans le corps d’une boucle (for ou while), on peut utiliser:

('♢', '♧', '♡', '♤')
('♢', '♧', '♤', '♡')
('♢', '♡', '♧', '♤')
('♢', '♡', '♤', '♧')
('♢', '♤', '♧', '♡')
V de ♢
D de ♢
R de ♢
As de ♢
V de ♡
D de ♡
R de ♡
As de ♡

break et continue sont toujours relatives
à la boucle la plus imbriquée

V de ♢
V de ♧
D de ♢
D de ♧
V de ♢
V de ♧
D de ♢
D de ♧
V de ♢
V de ♢
D de ♢

compréhension

un raccourci syntaxique pour construire des containers par itération

[100, 100, 177241]
{100, 177241}
{10: 100, -10: 100, 421: 177241}
{100}

expressions génératrices

la compréhension est souvent élégante
mais elle souffre du même problème qu’on a vu plus haut
elle alloue de la mémoire pour ranger les résultats
et dans ces cas-là un itérateur est un bien meilleur choix

dans ces cas-là, il suffit de prendre la compréhension de liste
et remplacer les [] par ()
cela s’appelle une expression génératrice

Le fait de devoir allouer de la mémoire a plusieurs inconvénients. Tout d’abord bien entendu, la mémoire est une ressource finie, qu’il faut donc utiliser avec parcimonie. De plus, lorsqu’un programme a besoin de mémoire il la demande au système d’exploitation, et c’est une opération bien plus lente qu’on pourrait le soupçonner naïvement ; et même sur une machine dotée d’une mémoire énorme, ce temps d’allocation pénalise les performances.

Benchmark : la somme des premiers entiers.

49.2 ms ± 2.01 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
32.8 ms ± 87.1 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
8448728
200

résumé

pour itérer sur une suite (un itérable) d’entrées

on n’itère jamais sur range(len(iterable))
on utilise enumerate à la place

dans tous les cas où c’est possible