Mon Labo Python

Comment bien utiliser la fonction range Python dans ses boucles for

Comment bien utiliser la fonction range Python dans ses boucles for

Il est un peu plus de vingt heures à Clermont, la pluie tape contre les vitres de ma cuisine et ma soupe à l'oignon finit doucement de refroidir à côté de mon clavier. J'ai les yeux qui piquent un peu. Je suis installée à cette table depuis que je suis rentrée du bureau, et je m'arrache les cheveux sur un petit script qui est censé automatiser le tri de mon tableur de suivi de budget. Je voulais juste que ma boucle compte de un à cinq. Rien de bien sorcier, pensais-je. Mais voilà, Python semble avoir une notion de l'arithmétique bien à lui. C'est le début de ma sixième semaine de pratique intensive, depuis le début du printemps dernier, et je réalise que même les outils les plus simples cachent des pièges où l'on tombe tête la première.

Ce soir où j'ai voulu compter jusqu'à cinq (et où Python a dit non)

Dans ma tête, c'était limpide. Pour dire à ma boucle de s'exécuter cinq fois, j'ai écrit quelque chose comme for i in range(5):. Je m'attendais à voir s'afficher 1, 2, 3, 4 et 5. Et là, c'est le drame : mon écran me renvoie 0, 1, 2, 3, 4. La sensation du bord en métal de mon vieux laptop qui chauffe sous mes poignets alors que je relance le script pour la dixième fois n'aide pas vraiment à calmer ma frustration. Pourquoi ce zéro ? Et où est passé mon cinq ?

C'est là que j'ai compris la première règle d'or, celle qui vous fait vous sentir un peu bête au début : l'indice de départ par défaut dans la fonction range, c'est 0. Toujours. Pour Python, commencer à compter par un, c'est apparemment trop classique. C'est un peu comme si, en faisant mes courses au marché de la place de Jaude, le premier stand n'était pas le numéro 1 mais le numéro 0. Il m'a fallu un moment pour m'habituer à cette logique de machine. Mais le plus dur, c'était cette absence du chiffre final. Ce moment de solitude quand j'ai réalisé que range(10) ne contient jamais le chiffre 10, me laissant avec une donnée manquante en fin de liste, a été ma première vraie leçon d'humilité face au code.

Schéma manuscrit expliquant les paramètres de la fonction range Python sur un carnet

Les trois manettes de la fonction range

Un mardi soir pluvieux en mai, j'ai décidé de prendre mon carnet à petits carreaux et de dessiner ce qui se passait vraiment sous le capot. J'ai découvert que cette fonction n'est pas juste un petit compteur automatique, c'est un outil avec exactement trois arguments maximum. On peut les imaginer comme trois manettes sur un vieux poste de radio. Le premier argument, c'est le 'start' (le début), le deuxième c'est le 'stop' (la fin), et le troisième, c'est le 'step' (le pas).

Si vous ne touchez qu'à une seule manette, Python considère que c'est le 'stop'. C'est pour ça que range(5) s'arrête juste avant 5. C'est l'exclusivité du dernier chiffre, un concept qui m'a rappelé mes cours de maths de troisième où on mettait des crochets ouverts ou fermés, sauf que là, c'est toujours ouvert à la fin. Par défaut, l'incrément est de 1. Chaque fois que la boucle tourne, elle avance d'une petite graduation sur sa règle imaginaire.

Pour mieux m'y retrouver dans mes scripts de plus en plus longs, j'ai commencé à appliquer mes astuces pour bien nommer ses variables Python en suivant la PEP 8. Au lieu d'utiliser seulement 'i', j'essaie de nommer mon compteur 'numero_ligne' ou 'index_element'. Ça n'aide pas Python à mieux compter, mais ça m'aide, moi, à ne pas oublier ce que je suis en train de faire quand le café commence à ne plus faire effet.

L'expérience de la canicule et le réglage du 'pas'

Pendant la canicule de juillet, alors que j'essayais de coder avec un ventilateur qui faisait un bruit de décollage d'avion, j'ai dû traiter une liste de données où je ne voulais récupérer qu'un élément sur deux. C'est là que j'ai découvert l'utilité du troisième paramètre : le 'step'. En écrivant range(0, 10, 2), Python m'a enfin donné mes nombres pairs : 0, 2, 4, 6, 8.

Le déclic est venu quand j'ai arrêté de voir range comme une liste toute faite. En réalité, en Python, range() ne génère pas une liste entière en mémoire, mais un objet itérable qui produit les nombres au besoin. C'est beaucoup plus malin ! C'est comme si, au lieu de préparer dix assiettes de soupe à l'avance et de les stocker sur le plan de travail, on avait une louche magique qui ne remplit l'assiette que quand quelqu'un s'assoit à table. Ça économise de la place, surtout quand on commence à manipuler des milliers de lignes de données. À ce propos, j'avais aussi galéré sur la différence entre liste et tuple en python, et comprendre que range n'est ni l'un ni l'autre m'a pris un temps fou.

Mains d'une débutante tapant une boucle for range sur son ordinateur portable

Le piège du débutant : range(len(ma_liste))

Il y a environ trois semaines, j'ai eu une discussion avec une connaissance qui s'y connaît un peu mieux que moi. Elle m'a vu écrire une boucle qui ressemblait à ça : for i in range(len(ma_liste)): element = ma_liste[i]. Elle a souri et m'a dit : "Pauline, pourquoi tu demandes à Python de compter des numéros pour ensuite aller chercher ce qui est à ces numéros ? Demande-lui directement les objets !"

C'est ma petite opinion de débutante qui commence à prendre de la bouteille : n'utilisez jamais range() pour itérer sur une liste si vous avez seulement besoin des éléments. Ça rend le code lourd, difficile à lire, et c'est un peu moins efficace. C'est comme si, pour donner un biscuit à chacun de mes neveux, je comptais d'abord les enfants, puis que je disais 'Enfant numéro 1, prends ton biscuit', 'Enfant numéro 2, prends ton biscuit'. C'est bien plus naturel de dire 'Pour chaque enfant dans ce groupe, donne un biscuit'. En Python, for element in ma_liste: fait exactement ça.

J'ai compris que range() est un outil de construction. On l'utilise quand on a besoin de l'index lui-même, par exemple pour modifier une position précise ou quand on n'a pas de liste sous la main et qu'on veut juste répéter une action un nombre précis de fois. Pour tout le reste, l'itération directe est la reine. En explorant ces nuances, j'ai d'ailleurs commencé à découvrir l'automatisation réseau avec python pour simplifier ses tâches, car c'est fou comme ces petites boucles for sont partout dès qu'on veut parler à des machines.

Petite victoire avant d'éteindre la lumière

Ma boucle tourne enfin. Elle ne s'arrête plus à la ligne d'avant, elle ne saute plus de données. Je n'ai plus d'erreur 'off-by-one' (c'est le nom savant pour dire qu'on s'est planté d'un chiffre, j'ai appris ça sur un forum). Ce n'est pas encore du grand art, mon script ressemble probablement à un gribouillage d'enfant pour un vrai développeur, mais il fait le job.

Il me reste encore des zones d'ombre. Par exemple, j'ai essayé une fois d'utiliser un pas négatif pour compter à l'envers, et j'ai réussi à créer une boucle infinie qui a presque fait figer mon ordinateur. Je n'ai pas encore tout à fait compris pourquoi range(10, 0) ne renvoie rien du tout si on n'ajoute pas le pas négatif explicitement. Mais bon, pour ce soir, je vais me contenter de cette victoire. Je ferme l'ordinateur, je range ma tasse de thé vide, et je vais me coucher avec l'impression d'avoir un tout petit peu moins de brouillard dans la tête. Demain, j'attaquerai peut-être les dictionnaires, ou je rangerai enfin mes scripts dans des dossiers propres. On verra bien ce que le café en dit au réveil.

Articles connexes