Contactez-nous

Itérateurs et générateurs

Découvrez les itérateurs et les générateurs en Python. Apprenez à créer des itérateurs personnalisés, à utiliser le mot-clé yield pour créer des générateurs, et à comprendre les avantages des générateurs en termes de performance et de mémoire.

Comprendre le protocole d'itération : comment Python parcourt les séquences

Lorsque vous utilisez une boucle `for` pour parcourir une liste, un tuple, une chaîne de caractères, ou un autre type de séquence, Python utilise implicitement un mécanisme appelé le protocole d'itération. Comprendre ce protocole vous permettra de créer vos propres objets itérables et de mieux comprendre le fonctionnement interne de Python.

Un objet itérable est un objet qui peut être parcouru avec une boucle `for`. Pour être itérable, un objet doit implémenter la méthode `__iter__()`, qui renvoie un itérateur.

Un itérateur est un objet qui représente un flux de données. Il doit implémenter la méthode `__next__()`, qui renvoie l'élément suivant du flux, ou lève l'exception `StopIteration` lorsqu'il n'y a plus d'éléments.

Lorsque vous utilisez une boucle `for`, Python appelle implicitement la méthode `__iter__()` de l'objet itérable pour obtenir un itérateur, puis appelle répétitivement la méthode `__next__()` de l'itérateur pour obtenir chaque élément, jusqu'à ce que `StopIteration` soit levée.

Nous verrons des exemples d'objets itérables et d'itérateurs, et comment Python utilise le protocole d'itération en coulisses.

Créer des itérateurs personnalisés : définissez votre propre logique de parcours

Vous pouvez créer vos propres itérateurs en définissant une classe qui implémente les méthodes `__iter__()` et `__next__()`. Cela vous permet de définir votre propre logique de parcours, et de créer des objets qui peuvent être utilisés avec une boucle `for`.

Par exemple, vous pourriez créer un itérateur qui parcourt les nombres pairs d'un intervalle donné, ou un itérateur qui parcourt les lignes d'un fichier en ignorant les lignes vides.

Créer des itérateurs personnalisés peut rendre votre code plus modulaire, plus lisible et plus efficace, en particulier lorsque vous travaillez avec de grandes quantités de données ou avec des séquences complexes.

Nous verrons des exemples d'itérateurs personnalisés, et comment les utiliser.

Utiliser le mot-clé `yield` pour créer des générateurs : la simplicité incarnée

Les générateurs sont un type spécial d'itérateur qui sont particulièrement faciles à créer en Python. Au lieu de définir une classe avec les méthodes `__iter__()` et `__next__()`, vous pouvez utiliser une fonction avec le mot-clé `yield`.

Une fonction génératrice est une fonction qui contient au moins un `yield`. Lorsque vous appelez une fonction génératrice, elle ne s'exécute pas immédiatement. Au lieu de cela, elle renvoie un objet générateur. Lorsque vous itérez sur l'objet générateur (par exemple, avec une boucle `for`), la fonction s'exécute jusqu'à ce qu'elle rencontre un `yield`. Le `yield` renvoie une valeur, puis la fonction se met en pause, en conservant son état.

A l'itération suivante, la fonction reprend son exécution à partir de l'endroit où elle s'était arrêtée, jusqu'à ce qu'elle rencontre un autre `yield`, ou jusqu'à ce qu'elle se termine. S'il n'y a plus de `yield`, l'exception `StopIteration` est levée, comme pour un itérateur normal.

Les générateurs sont un moyen simple et élégant de créer des itérateurs, en particulier lorsque la logique de parcours est complexe ou lorsque vous travaillez avec des séquences potentiellement infinies.

Nous verrons des exemples de fonctions génératrices, et comment les utiliser.

Expressions génératrices : encore plus de concision

Les expressions génératrices sont similaires aux compréhensions de listes, mais au lieu de créer une liste en mémoire, elles créent un générateur à la volée. Elles sont particulièrement utiles lorsque vous travaillez avec de grandes séquences de données, car elles ne chargent pas toute la séquence en mémoire en une seule fois.

La syntaxe d'une expression génératrice est similaire à celle d'une compréhension de liste, mais vous utilisez des parenthèses `()` au lieu de crochets `[]`. Par exemple : `(x**2 for x in range(10))` crée un générateur qui produit les carrés des nombres de 0 à 9.

Vous pouvez utiliser une expression génératrice directement dans une boucle `for`, ou la passer à une fonction qui attend un itérable (comme `sum`, `list`, `tuple`).

Nous verrons des exemples d'expressions génératrices, et comment les utiliser pour écrire du code plus concis et plus efficace en termes de mémoire.

Avantages des générateurs : performance et économie de mémoire

Les générateurs offrent plusieurs avantages par rapport aux listes ou aux autres structures de données qui stockent tous les éléments en mémoire.

Le principal avantage est l'économie de mémoire. Les générateurs produisent les éléments à la demande, au fur et à mesure que vous itérez dessus. Ils n'ont pas besoin de stocker toute la séquence en mémoire en une seule fois. Cela peut faire une énorme différence lorsque vous travaillez avec de grandes quantités de données, ou avec des séquences potentiellement infinies.

Un autre avantage est la performance. Dans certains cas, les générateurs peuvent être plus rapides que les listes, car ils évitent le coût de la création et de la gestion d'une grande liste en mémoire.

Enfin, les générateurs peuvent rendre votre code plus lisible et plus expressif, en particulier lorsque vous travaillez avec des séquences complexes. Ils vous permettent de séparer la logique de génération des données de la logique de traitement des données.

Nous verrons des exemples concrets pour illustrer ces avantages, et nous discuterons des situations où les générateurs sont particulièrement utiles.