6.3. Itérations avec des boucles for

Comme la plupart des langages, Python a des boucles for. La seule raison pour laquelle vous ne les avez pas vues jusqu’à maintenant est que Python sait faire tellement d’autre choses que vous n’en avez pas besoin aussi souvent.

La plupart des autres langages n’ont pas de type de données liste aussi puissant que celui de Python, vous êtes donc amené à faire beaucoup de travail à la main, spécifier un début, une fin et un pas pour définir une suite d’entiers ou de caractères ou d’autres entités énumérables. Mais en Python une boucle for parcourt simplement une liste, de la même manière que les list comprehensions fonctionnent.

Exemple 6.8. Présentation des boucles for

>>> li = ['a', 'b', 'e']
>>> for s in li:         1
...     print s          2
a
b
e
>>> print "\n".join(li)  3
a
b
e
1 La syntaxe d’une boucle for est similaire aux list comprehensions. li est une liste et s prend successivement la valeur de chaque élément, en commençant par le premier.
2 Comme une instruction if ou n’importe quel autre bloc indenté, une boucle for peut contenir autant de lignes de codes que vous le voulez.
3 Voici la raison pour laquelle vous n’aviez pas encore vu la boucle for : nous n’en avions pas eu besoin. C’est incroyable la fréquence à laquelle nous utilisons les boucles for dans d’autres langages alors que ce que nous voudrions vraiment et un join ou une list comprehension.

Faire un compteur «normal» (selon les critères de Visual Basic) pour la boucle for est également simple.

Exemple 6.9. Compteurs simples

>>> for i in range(5):             1
...     print i
0
1
2
3
4
>>> li = ['a', 'b', 'c', 'd', 'e']
>>> for i in range(len(li)):       2
...     print li[i]
a
b
c
d
e
1 Comme nous l’avons vu dans l'Exemple 3.20, «Assignation de valeurs consécutives», range produit une liste d’entiers que nous pouvons parcourir. Je sais que ça peut sembler étrange, mais c’est parfois (et j’insiste sur le parfois) utile d’avoir une boucle sur un compteur.
2 Ne faites jamais ça. C’est un style de pensée Visual Basic. Libérez-vous en. Parcourez simplement la liste comme dans l’exemple précédent.

Les boucles for ne sont seulement faites pour les compteurs simples. Elles peuvent parcourir de nombreuses choses. Voici un exemple d'utilisation d'une boucle for pour parcourir un dictionnaire.

Exemple 6.10. Parcourir un dictionnaire

>>> import os
>>> for k, v in os.environ.items():      1 2
...     print "%s=%s" % (k, v)
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim

[...snip...]
>>> print "\n".join(["%s=%s" % (k, v)
...     for k, v in os.environ.items()]) 3
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim

[...snip...]
1 os.environ est un dictionnaire des variables d’environnement définies dans votre système. Sous Windows ce sont vos variables utilisateur et système. Sous UNIX ce sont les variables exportées par le script de démarrage de votre shell. Sous Mac OS il n’y a pas de notion de variables d’environnement, ce dictionnaire est donc vide.
2 os.environ.items() retourne une liste de tuples : [(key1, value1), (key2, value2), ...]. La boucle for parcourt cette liste. A la première itération, il assigne key1 à k et value1 à v, donc k = USERPROFILE et v = C:\Documents and Settings\mpilgrim. A la seconde, k reçoit la deuxième clé, OS et v la valeur correspondante, Windows_NT.
3 Avec l’assignement multiple de variable et les list comprehensions, vous pouvez entièrement remplacer la boucle for par une seule instruction. Le choix d’une des deux formes dans votre code est une question de style personnel. J’aime ce style parce qu’il rend clair que ce que nous faisons est une mutation d’un dictionnaire en une liste, puis de joindre cette liste en une chaîne unique. D’autres programmeurs préfèrent la forme de la boucle for. Notez que la sortie est la même dans les deux cas, bien que cette version-ci soit légèrement plus rapide car il n’y a qu’une instruction print au lieu d’une par itération.

Maintenant nous pouvons examiner l'usage de la boucle for dans la classe MP3FileInfo du programme d'exemple fileinfo.py présenté au Chapitre 5.

Exemple 6.11. Boucle for dans MP3FileInfo

    tagDataMap = {"title"   : (  3,  33, stripnulls),
                  "artist"  : ( 33,  63, stripnulls),
                  "album"   : ( 63,  93, stripnulls),
                  "year"    : ( 93,  97, stripnulls),
                  "comment" : ( 97, 126, stripnulls),
                  "genre"   : (127, 128, ord)}                               1
    .
    .
    .
            if tagdata[:3] == "TAG":
                for tag, (start, end, parseFunc) in self.tagDataMap.items(): 2
                    self[tag] = parseFunc(tagdata[start:end])                3
1 tagDataMap est un attribut de classe qui définit les balises que nous recherchons dans un fichier MP3 file. Les balises sont stockées dans des champ de longueur fixe, une fois que nous avons lu les derniers 128 octets du fichier, les octets 3 à 32 contiennent toujours le titre de la chanson, 33-62 le nom de l’artiste, 63-92 le nom de l’album etc. Notez que tagDataMap est un dictionnaire de tuples et que chaque tuple contient deux entiers et une référence de fonction.
2 Ceci à l’air compliqué, mais ne l’est pas. La structure des variables de for correspond à la structure des éléments de la liste retournée par items. Rappelez-vous, items retourne une liste de tuples de la forme (key, value). Le premier élément de cette liste est ("title", (3, 33, <function stripnulls>)), donc à la première itération de la boucle tag reçoit "title", start reçoit 3, end reçoit 33 et parseFunc reçoit la fonction stripnulls.
3 Maintenant que nous avons extrait tous les paramètres pour une balise MP3 unique, sauvegarder les données de la balise data est simple. Nous découpons tagdata de start à end pour obtenir les véritables données de cette balise, nous appelons parseFunc pour le traitement final des données et assignons le résultat comme valeur de la clé tag dans le pseudo-dictionnaire self. Après itération de tous les éléments de tagDataMap, self a les valeurs de toutes les balises et vous savez à quoi ça ressemble.