Loop for en Python : la comprendre et l’utiliser

La loop for en Python sert à exécuter un bloc de code de manière répétée. Les boucles for sont un élément fondamental de la plupart des langages de programmation. Nous montrons comment fonctionne la boucle for en Python et comment l’utiliser.

Domaine Internet pas cher

Bien plus qu'un simple domaine !

Personnalisez votre présence en ligne avec un nom de domaine pertinent.

Email
Certificat SSL
Assistance 24/7

Qu’est-ce que la loop for en Python ?

La boucle for est, avec l’embranchement if-else, la technique de programmation la plus connue. Des générations de programmeurs se sont démenés pour développer le concept informatique de la boucle. En effet, les boucles sont au départ peu intuitives. Pourtant, les problèmes de compréhension proviennent peut-être davantage de la manière avec laquelle le concept est présenté. Car en soi, les boucles n’ont rien de très particulier.

Illustrer le concept de la boucle par un exemple concret sert cependant à la compréhension. Imaginons une classe d’école : l’enseignant souhaite déterminer la taille moyenne des enfants dans le cadre d’une expérience. Pour ce faire, il demande à chaque enfant sa taille au fur et à mesure et additionne les différentes tailles pour obtenir une somme. Ensuite, il divise la somme par le nombre d’enfants pour obtenir la taille moyenne. Nous faisons un algorithme simple à partir de la procédure de l’enseignant :

  1. Demander la taille de chaque enfant et l’ajouter à la somme
  2. Diviser la somme par le nombre d’enfants

La première étape est effectuée une fois pour chaque enfant et dépend donc du nombre d’enfants dans la classe. La deuxième étape n’est exécutée qu’une seule fois, indépendamment du nombre d’enfants dans la classe. Nous avons besoin d’une boucle pour la première étape. Voici un exemple de code qui calcule la taille moyenne d’un écolier avec un loop for en Python :

children_heights = [155, 171, 148, 161, 158, 153, 162]
height_sum = 0
for height in children_heights:
    height_sum = height_sum + height
average_height = height_sum // len(children_heights)
print(average_height)

La loop for en Python exécute un bloc de code de manière répétée. On parle également d’« itération ». Les boucles permettent notamment de traiter individuellement, selon le même schéma, plusieurs éléments regroupés dans une même « collection ». Une boucle for est donc utilisée lorsque tous les éléments de la collection peuvent être déterminés pendant l’exécution du programme. Si ce n’est pas le cas, alors une boucle while est recommandée.

Conseil

Apprenez à utiliser le langage de programmation Python avec notre tutoriel Python!

Qu’est-ce qui différencie la loop for de Python des autres langages ?

De nombreux langages de programmation connaissent le concept de la boucle for. Il s’agit d’une construction fondamentale dans d’autres langages comme C, Java, JavaScript et PHP. Les langages purement fonctionnels comme Haskell ou Lisp n’ont généralement pas de boucle for. Au lieu de l’itération, ces langages utilisent des fonctions récursives.

Toutes les boucles ont en commun une chose : un bloc de code est exécuté de manière répétée. Cependant, le fonctionnement de la loop for en Python se distingue nettement des autres. La plupart des langages de programmation utilisent une variable de boucle qui est incrémentée ou décrémentée lorsque la boucle est exécutée.

Action Signification Représentation ordinaire Représentation en Python
Incrémentation Augmenter la valeur d’une variable entière d’un montant fixe déterminé i++ index += 1
Décrémentation Diminuer la valeur d’une variable entière d’un montant fixe déterminé i-- index -= 1

Prenons un exemple pour voir comment fonctionne une boucle for dans d’autres langages. Nous prenons tout d’abord les nombres de 0 à 9 en JavaScript avec une boucle for. Une variable entière 'number' est ensuite définie et on l’incrémente tant que le nombre qu’elle contient est inférieur à 10. Le code utilisé pour cette opération semble plutôt complexe pour les débutants :

for ( let number = 0; number < 10; number++ ) {
    console.log(number);
}

Alors que le code d’une boucle for correspondante en Python semble nettement plus clair :

for number in range(10):
    print(number)

Au lieu d’afficher directement la valeur de la variable de boucle, celle-ci est généralement utilisée pour indexer un élément au sein d’une collection. Prenons à nouveau un exemple en JavaScript : nous affichons les noms contenus dans la liste 'people' les uns après les autres. Pour cela, nous utilisons la variable de boucle 'i' comme index continu des différents éléments :

people = ['Jack', 'Jim', 'John']
for (let i = 0; i < people.length; i++) {
    console.log("Here comes " + people[i]);
}

Indexer directement des éléments de liste séquentiels doit être fait avec prudence. En effet, une tentative d’accès en dehors des limites autorisées entraîne une erreur d’exécution. En règle générale, il s’agit de la fameuse « Off-by-one Error ». Python montre qu’il est possible de faire autrement. Voici le même exemple avec une boucle for en Python : nous itérons directement sur les éléments de la liste, sans les indexer avec une variable de la boucle :

people = ['Jack', 'Jim', 'John']
for person in people:
    print(f"Here comes {person}")

La valeur indirecte de la variable de boucle contribue largement à la complexité de l’apprentissage des boucles for dans d’autres langages. En effet, la variable de boucle centrale ne nous intéresse généralement pas. Elle ne sert qu’à indexer les différents éléments. L’utilisation des boucles for traditionnelles nécessite la compréhension de plusieurs sujets complexes. Prenons l’exemple de JavaScript :

Thème Représentation dans la boucle for JavaScript
Déclarer une variable let i = 0
Expressions booléennes i < limit
Outil d’incrémentation et de décrémentation i++ / i--
Déterminer la taille d’une collection i < list.length
Indexation d’éléments à partir de zéro i < list.length est OK; i <= list.length mène à Off-by-one Error

La boucle for en Python est nettement plus simple d’utilisation. Même si le mot-clé 'for' est utilisé, il s’agit d’une approche fondamentalement différente. Au lieu d’incrémenter une variable de la boucle et d’indexer ainsi successivement des éléments, nous itérons directement sur les éléments d’une collection. La loop for en Python est ainsi comparable à la construction forEach de certains langages comme Ruby et JavaScript.

Pour illustrer notre propos, donnons les différentes lettres d’un mot. Dans la boucle for, une lettre du mot est placée dans la variable 'letter' à chaque passage de la boucle. Cela fonctionne sans variable de boucle et donc sans risque de produire une erreur off-by-one. Le code est précis et facile à lire :

word = "Python"
for letter in word:
    print(letter)

Comment fonctionne une loop for en Python ?

Comme nous l’avons vu, la loop for en Python résout efficacement le problème de l’itération sur les éléments d’une collection. Pour ce faire, nous n’avons donc pas besoin de passer par une variable de boucle numérique. C’est certes un très bon point, mais comment cela fonctionne-t-il exactement ? Pour comprendre le principe de fonctionnement de la loop for en Python, il faut connaître les concepts d’itérable et d’itérateur.

Qu’est-ce que l’itérable, l’itérateur et le générateur ?

La loop for de Python opère sur des objets connus sous le nom d’« itérables ». Il s’agit de chaînes de caractères, de listes, de tuples et d’autres types de données composites. Selon les termes de la documentation officielle de Python :

Citation

"[An iterable is] an object capable of returning its members one at a time"/ Source : https://docs.python.org/3/glossary.html#term-iterable

Traduction : « [Un itérable est] un objet dont on peut parcourir les valeurs une à une » (traduit par IONOS)

Un objet itérable a deux propriétés :

  1. Il regroupe plusieurs éléments pour former une collection
  2. Il donne un accès à ses éléments via une interface appelée « itérateur »

Ensemble, cela signifie que les itérables sont des collections dont le contenu peut être itéré. Une itérable possède spécifiquement une méthode '__iter__()' qui renvoie un itérateur. L’itérateur est un objet qui fournit sur commande l’élément suivant de l’itérable. De plus, un itérateur rappelle la position du dernier élément renvoyé dans la collection.

Fonction Explication Exemple
iter(collection) Appelle la méthode __iter__() de la collection it = iter("Python")
next(iter) Appelle la méthode __next__() de l'itérateur next(it)
collection[index] Appelle la méthode __getitem__(index) de la collection 'Python'[1]

Un itérateur renvoie à l’élément suivant lors de l’appel de la méthode __next__(). Si tous les éléments de la collection ont été livrés, l’itérateur est épuisé. Un autre appel à __next__() déclenche une exception 'StopIteration'.

Considérons le fonctionnement d’un itérateur à l’aide d’un exemple. Nous créons un objet range() qui représente les nombres consécutifs 21 à 23. Ensuite, nous créons un itérateur à l’aide de la fonction iter() et nous sortons des éléments successifs à l’aide de la fonction next(). Lors du dernier appel, l’exception est levée car l’itérateur est épuisé :

numbers = range(21, 24)
number = iter(numbers)
next(number)
# returns '21'
next(number)
# returns '22'
next(number)
# returns '23'
next(number)
# raises 'StopIteration' exception

Un itérateur donne accès à des éléments individuels d’une collection. Python connaît également le concept similaire de « générateur ». La différence réside dans le fait qu’un générateur ne crée des éléments individuels qu’au moment de l’accès. Cela permet surtout d’économiser de la mémoire lors de l’exécution du programme, c’est pourquoi on parle aussi de « lazy generation ».

Un générateur en Python est basé sur une fonction qui utilise l’instruction Yield. Celle-ci renvoie un objet de la même manière que l’instruction return et met fin à l’appel de la fonction. Lors d’un nouvel appel, la fonction de générateur ne recommence cependant pas depuis le début, mais continue après la dernière instruction yield.

L’exemple suivant illustre ces propos. Nous écrivons notre propre version de la fonction range(). Nous utilisons l’instruction yield dans une boucle while pour générer des nombres continus :

def my_range(start, stop):
    if stop < start:
        return None
    current = start
    while current < stop:
        yield current
        current += 1
# test
assert list(my_range(7, 9)) == list(range(7, 9))

Sauter et annuler un passage de la loop for en Python

Dans la pratique, il est parfois nécessaire d’ignorer un seul passage de la boucle. Comme beaucoup d’autres langages, Python contient l’instruction « continue ». Lors de l’appel de continue dans le corps de la boucle, l’itération en cours est interrompue. La boucle commence alors immédiatement l’itération suivante.

Une instruction continue peut être utilisée de manière similaire au early-return lors de l’appel d’une fonction. Par exemple, nous sautons une itération dès que nous constatons qu'un ensemble de données n'a pas la qualité requise :

def process_data(data):
    for data_set in data:
        data_set.validate()
        # early continue after cheap check fails
        if not data_set.quality_ok():
            continue
        # expensive operation guarded by early continue
        data_set.process()

Un autre exemple : nous prenons un texte et sautons une lettre sur deux :

text = 'Skipping every second letter'
for index, letter in enumerate(text):
    if index % 2 != 0 and letter != ' ':
        continue
    print(letter)

En plus de l’instruction continue pour sauter un passage de la boucle, il existe l’instruction break. Un appel à break à l’intérieur du corps de la boucle interrompt immédiatement la suite de l’exécution de la boucle. Ainsi, break a une fonction similaire pour les boucles que l’instruction return pour les fonctions.

L’instruction break est souvent utilisée pour mettre en œuvre des algorithmes de recherche. Si un élément recherché est trouvé dans une boucle, il est inutile de continuer à itérer. De la même manière que pour la fonction any(), nous vérifions la présence d’une seule valeur True dans une liste. Avec break, on s’arrête dès que nous avons trouvé quelque chose :

bool_list = [False, False, True, False]
for index, boolean in enumerate(bool_list):
    if boolean:
        print(f"Value at position {index + 1} is True")
        print(f"Aborting inspection of remaining {len(bool_list) - index - 1} item(s)")
        break

En relation avec l’instruction break, une loop for en Python peut être dotée d’un corps else optionnel. Le code qu’il contient est exécuté lorsque la boucle se termine sans qu’une instruction break ait été exécutée :

def find_element(target, collection):
    for element in collection:
        if element == target:
            print("Found what you’re looking for")
            break
    else:
        print("Didn’t find what you were looking for")
# test
find_element('a', 'Python')
find_element('o', 'Python')

Les boucles for sont souvent utilisées en Python à l’intérieur de corps de fonctions. Dans ce cas, il est courant d’utiliser une instruction return au lieu d’une instruction break. Notre algorithme de recherche reformule sans utiliser break et else :

def find_element(target, collection):
    for element in collection:
        if element == target:
            print("Found what you’re looking for")
            # returning breaks us out of the loop
            return element
    # we made it here without returning
    print("Didn’t find what you were looking for")
    return None
# test
print(find_element('a', 'Python'))
print(find_element('o', 'Python'))

Quelles sont les meilleures façons d’utiliser les loops for en Python ?

Les loops for en Python servent en premier lieu à itérer sur les éléments d’une séquence ou d’une collection. Il existe des méthodes plus directes pour de nombreux cas d’application courants. Nous présentons les meilleures pratiques et les antipatterns importants. Tout d’abord, un aperçu des termes clés :

Terme Explication Exemple
Collection Regroupement de plusieurs éléments ; une collection est une itérable ('Walter', 'White'), [4, 2, 6, 9], 'Python'
Itérateur Interface pour itérer sur les collections it = iter('Python')
Génerateur Une fonction qui utilise yield au lieu de l’instruction return ; un générateur est itérable range(10)
Compréhension Expression itérative ; crée une nouvelle collection basée sur une itérable [num ** 2 for num in range(10)]

Itérer directement sur les éléments d’une collection

Une erreur fréquente des programmeurs inexpérimentés est d’abuser de la loop for en Python. Comme c’est souvent le cas dans d’autres langages, ils utilisent la fonction len() comme limite de la fonction range() pour créer une variable numérique en boucle. Ils s’en servent pour indexer les différents éléments de la collection :

word = 'Python'
for i in range(len(word)):
    print(word[i])

Cet antipattern est réprouvé pour une bonne raison : il n’est pas pythonien. En effet, il est préférable d’itérer directement sur les éléments de la collection avec la loop for de Python :

word = 'Python'
for letter in word:
    print(letter)

Énumérer les éléments d’une collection avec enumerate() et leur index

On a parfois besoin de l’index d’un élément dans une collection. Au lieu de créer l’index comme variable de boucle, nous utilisons la fonction enumerate(). Celle-ci renvoie le tuple (index, élément). Il faut noter que l’index commence à compter à partir de zéro :

names = ["Jim", "Jack", "John"]
for index, name in enumerate(names):
    print(f"{index + 1}. {name}")

Utiliser la fonction zip() pour itérer sur des tuples d’éléments.

Un autre scénario fréquemment rencontré est l’itération simultanée sur les éléments de deux collections de même longueur. L’approche pythonienne utilise la fonction zip(). Celle-ci prend deux collections de même longueur et renvoie successivement 2 tuples :

people = ('Jim', 'Jack', 'John')
ages = (42, 69, 13)
# ascertain both collections are same length
assert len(people) == len(ages)
# iterate over tuples of (person, age)
for person, age in zip(people, ages):
    print(f"{person} is {age} years old")

Créer une variable numérique en boucle avec la fonction range()

Normalement, les boucles for sont utilisées en Python pour itérer sur les éléments d’une collection. L’incrémentation d’un nombre entier avec une loop for est en Python plutôt un cas spécial. La manière correcte de procéder est de construire un objet range avec la fonction range() et d’itérer sur cet objet :

for counter in range(10):
    print(counter)

Tester si une collection contient un élément avec l’opérateur in

Trouver un élément précis dans une collection fait partie du répertoire standard d’un programmeur. Normalement, une fonction est utilisée pour itérer sur les éléments et vérifier que chaque élément est identique à celui recherché. Si l’élément est trouvé, alors l’itération est interrompue.

En Python, l’opérateur in existe pour ce cas fréquent. L’opérateur vérifie si la collection contient l’élément recherché et renvoie une valeur booléenne correspondante :

'a' in 'Python'
'y' in 'Python'

Créer une liste à partir d’une itérable avec la fonction list()

Contrairement à de nombreux autres langages, il n’est pas nécessaire d’utiliser une loop for en Python pour écrire les lettres d’une chaîne une à une dans une liste. À la place, nous utilisons la fonction list() pour convertir une itérable en une liste d’éléments. Examinons les deux approches à l’aide d’un exemple. Nous itérons sur les lettres d’un mot et les ajoutons à une liste vide :

word = 'Python'
letters = []
for letter in word:
    letters.append(letter)

Nous pouvons nous épargner cet effort. Nous créons directement la liste avec la fonction list(). Dans le même temps, nous vérifions avec l’instruction assert que les deux méthodes donnent le même résultat :

assert list(word) == letters

Voici un autre exemple : nous créons la liste des nombres de zéro à neuf. Un objet range sert de base en tant qu’itérable :

list(range(10))

En plus des listes, il est également possible de créer des ensembles, en anglais « sets », à partir d’une itérable. Par exemple, nous créons un ensemble qui reflète les lettres contenues dans une phrase. Ensuite, nous vérifions avec l’opérateur in que l’ensemble des lettres ne contient pas de 'a' :

alphabet = set('Python is not hyped')
assert 'a' not in alphabet

Remplacer les loops for en Python par des compréhensions

Une utilisation fréquente des boucles for en Python consiste à modifier les éléments d’une collection. Le cas échéant, nous souhaitons calculer de nouvelles valeurs sur la base d’une collection ou filtrer certains éléments selon un modèle. Nous décrivons les différentes étapes en suivant le style de programmation impératif :

  1. Itérer sur la collection avec la boucle for
  2. Traiter chaque élément
  3. Le cas échéant, regrouper un sous-ensemble d’éléments dans une nouvelle collection

Pour de simples modifications, cela demande beaucoup de travail. Les langages fonctionnels montrent qu’il est possible de faire plus simple. Heureusement, Python connaît le concept des « compréhensions ». Les compréhensions peuvent remplacer les utilisations simples de la loop for dans Python et sont même parfois plus performantes.

Une compréhension génère une collection, éventuellement modifiée, basée sur une itérable. Une syntaxe concise et expressive est utilisée. Considérons la syntaxe générale d’une liste de compréhensions. L’expression est écrite entre crochets. Une opération est effectuée sur les éléments d’une collection ; chaque élément est copié dans une nouvelle liste :

[ operation(element) for element in collection ]

Il est également possible de filtrer les éléments selon certains modèles, ici avec if-condition :

[ operation(element) for element in collection if condition(element) ]

Considérons maintenant un exemple de loop For en Python, qui peut être remplacée par une compréhension. Nous avons une liste de nombres et nous voulons calculer la liste correspondante de carrés :

numbers = [2, 3, 5, 9, 17]

Nous créons une liste vide et la remplissons avec les carrés dans une boucle for :

squares = []
for number in numbers:
    squares.append(number ** 2)

Plus simplement, la liste de carrés peut être exprimée sous forme de compréhension :

squares_comp = [number ** 2 for number in numbers]

Ensuite, nous utilisons l’instruction assert pour nous assurer que les deux méthodes donnent le même résultat :

assert squares == squares_comp

Voici un autre exemple : nous voulons extraire des lettres minuscules d’une chaîne de caractères. En entrée, nous créons une liste de lettres de majuscules et de minuscules :

word = list("PyThoN")

La manière traditionnelle d’extraire les lettres minuscules consiste à itérer sur la lettre. Nous testons chaque lettre avec la fonction islower() et, si le résultat est positif, nous l’ajoutons à une liste initialement vide :

lowers = []
for letter in word:
    if letter.islower():
        lowers.append(letter)

Nous pouvons nous passer de cette loop for en Python. Nous utilisons à la place une compréhension qui ne copie que les lettres minuscules de la liste initiale :

lowers_comp = [ letter for letter in word if letter.islower() ]

Nous vérifions à nouveau l’égalité des deux méthodes à l’aide de l’instruction assert :

assert lowers == lowers_comp