Structure for

Syntaxe élémentaire

On a vu jusqu’ici que pour parcourir un tuple ou une list on utilisait un indice explicite. On a en fait une instruction composée dédiée à cette tâche en python : for.

La structure générale est la suivante:

for VARIABLE in CONTENEUR:
    INSTRUCTION1
    INSTRUCTION2
    ...
    INSTRUCTION_FINALE
NOUVELLE_INSTRUCTION

la VARIABLE va alors prendre les valeurs successives du conteneur.

Utilisation de range

Dans le schéma général précédent, on a mentionné qu’on pouvait insérer n’importe quel conteneur dans une boucle for. Mais force est de constater qu’on n’utilise en fait les éléments que les uns après les autres, on n’a pas littéralement besoin de toutes les valeurs simultanément.

L’instruction for est en fait plus générale, on peut lui fournir n’importe quel iterable. De manière plus précise il s’agit de tout objet python pouvant produire des valeurs successives à la demande.

Avant de voir une description générale de tels objets, on va commencer par un exemple extrêmement utile: range.

Il permet de produire une suite de nombres de différentes façons:

ATTENTION notez bien que range ne produit pas une list (à moins d’utiliser encore python2)

>>> nombres = range(1, 5)
>>> type(nombres)
<class 'range'>
>>> nombres
range(1, 5)

ATTENTION le sens des arguments dépend de l’ordre et du nombre total d’arguments. En cas de doute au début on n’hésitera pas à faire help(range).

Notion d’itérable

En fait on peut mettre dans for tout objet accepté par la fonction iter (on dit qu’il s’agit d’un itérable), celle-ci renverra un objet accepté par la fonction next qui permet de demander l’élément suivant (on qualifie un tel objet d’itérateur).

Une list est ainsi itérable mais pas un itérateur:

>>> iterable = [1, 2, 3, 4]
>>> next(iterable)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator
>>> iterateur = iter(iterable)
>>> next(iterateur)
1
>>> next(iterateur)
2
>>> next(iterateur)
3
>>> next(iterateur)
4
>>> next(iterateur)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Fondamentalement, une boucle

for ELEMENT in ITERABLE:
    ...

fonctionne de la manière suivante:

On verra comment donner un code équivalent à for quand on aura vu le mécanisme d’interception des erreurs. (il s’agit de l’instruction composée try: except:)

L’intérêt de ce procédé est d’éviter de l’occupation mémoire inutile quand on ne veut que les éléments les uns à la suite des autres et non simultanément.

Utilisation de enumerate, reversed et zip

ATTENTION notez que toutes les fonctions de cette section acceptent des itérables et renvoient des itérateurs!

On a tendance en python à éviter de gérer soit même les indices d’un conteneur (car c’est une source de bogues).

On n’est donc censé éviter presque systématiquement l’idiome:

for indice in range(len(conteneur)):

Si jamais on a besoin des indices on utilise enumerate:

>>> for indice, lettre in enumerate("abcdefghijklm"):
...     print(f"la {indice + 1}-ième lettre est {lettre}")
...
la 1-ième lettre est a
la 2-ième lettre est b
la 3-ième lettre est c
la 4-ième lettre est d
la 5-ième lettre est e
la 6-ième lettre est f
la 7-ième lettre est g
la 8-ième lettre est h
la 9-ième lettre est i
la 10-ième lettre est j
la 11-ième lettre est k
la 12-ième lettre est l
la 13-ième lettre est m

On récupère ainsi un couple (indice, élément) qu’on a tendance à déstructurer instantanément comme ci-dessus.

Si on veut parcourir en sens inverse on peut là encore se passer d’un indiçage rusé:

>>> for lettre in reversed("abcdef"):
...     print(lettre)
...
f
e
d
c
b
a

Finalement si on veut parcourir conjointement plusieurs itérables on utilise zip:

>>> for triplet in zip("abc", "def", "ghi"):
...     print(triplet)
...
('a', 'd', 'g')
('b', 'e', 'h')
('c', 'f', 'i')

ATTENTION si les itérables n’ont pas autant d’éléments les uns que les autres, on s’arrête à la fin du plus court:

>>> for test in zip("abcdef", "gh"):
...     print(test)
...
('a', 'g')
('b', 'h')

Exercices

  1. On suppose qu’on a une variable n: int, fournissez un code générant la liste des multiples de 5 entre 0 et n.
  2. En supposant qu’on ait une variable nombres: list[int], donnez le code permettant de calculer la moyenne et la variance.
  3. On suppose donnée une variable texte: str, donnez un code permettant de calculer le nombre de fois où la lettre e apparaît.

Pour aller plus loin

Pour un descriptif de l’instruction composée for regardez le paragraphe correspondant dans:

>>> help("LOOPING")

Pour les sequences (tuple, list et range) consultez

>>> help("SEQUENCES")
>>> help("SEQUENCEMETHODS")

Finalement on n’hésitera pas à consulter l’aide des fonctions introduites ci-dessus.

Corrections

  1. On donne une première version naïve (mais générale):

    >>> n = 123
    >>> resultat = list()
    >>> for nombre in range(0, 1 + n):
    ...     if nombre % 5 == 0:
    ...             resultat.append(nombre)
    ...
    >>> resultat
    [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120]

    Une version plus optimale (mais plus restreinte) utilisant que l’on a en fait des nombres de 5 en 5 :

    >>> n = 123
    >>> resultat = list()
    >>> for nombre  in range(0, 1 + n, 5):
    ...     resultat.append(nombre)
    ...
    >>> resultat
    [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120]
  2. Là encore on propose d’abord une solution naïve générale pour la moyenne:

    >>> nombres = [1, 5, 2, 7, 123, 5, 2, 6]
    >>> somme, longueur = 0, 0
    >>> for nombre in nombres:
    ...     somme = somme + nombre
    ...     longueur = longueur + 1
    ...
    >>> moyenne = somme / longueur
    >>> moyenne
    18.875

    Mais on peut aussi dans ce cas précis faire:

    >>> nombres = [1, 5, 2, 7, 123, 5, 2, 6]
    >>> moyenne = sum(nombres) / len(nombres)
    >>> moyenne
    18.875

    ATTENTION on a anticipé un peu sur la suite car le résultat après division est un float (nombre à virgule flottante) Pour la variance, une fois la moyenne calculée on adapte la première méthode de la moyenne:

    >>> nombres = [1, 5, 2, 7, 123, 5, 2, 6]
    >>> moyenne = sum(nombres) / len(nombres)
    >>> somme_quadratique = 0
    >>> for nombre in nombres:
    ...     somme_quadratique = somme_quadratique + (nombre - moyenne) ** 2
    ...
    ...
    >>> variance = somme_quadratique / len(nombres)
    >>> variance
    1552.859375

    On peut aussi tirer parti d’une formule classique pour avoir le code faisant un seul passage dans nombres:

    >>> nombres = [1, 5, 2, 7, 123, 5, 2, 6]
    >>> somme, longueur, somme_quadratique = 0, 0, 0
    >>> for nombre in nombres:
    ...     somme = somme + nombre
    ...     longueur = longueur + 1
    ...     somme_quadratique = somme_quadratique + nombre ** 2
    ...
    >>> variance = somme_quadratique / longueur - (somme / longueur) ** 2
    >>> variance
    1552.859375
  3. Il suffit ici d’utiliser une boucle for et un test if:

    >>> texte = """mlkfjqmeshzalekjhsqldkjfhqzlkeura
    ... qsmdkfjza
    ... qsdmlaeeraer
    ... e
    ... e
    ... eeaaz
    ... e
    ... """
    >>> nombre_de_e = 0
    >>> for lettre in texte:
    ...     if lettre == "e":
    ...             nombre_de_e = nombre_de_e + 1
    ...
    >>> nombre_de_e
    11