- Introduction aux générateurs
Un générateur est une fonction qui utilice le mot-clé yield au lieu de return. Contrairement à une fonction classique, l'exécution s'arrête à chaque yield et reprend lors de l'appel suivant à next().
Exemple simple :
def generateur_simple():
yield "premier"
yield "second"
yield "troisième"
g = generateur_simple()
print(next(g)) # premier
print(next(g)) # second
print(next(g)) # troisième
Dans la pratique, on utilise rarmeent next() directement. La boucle for est plus naturelle et appelle automatiquement next() derrière le rideau :
for valeur in generateur_simple():
print(valeur)
Les générateurs évitent les calculs inutiles et économisent la mémoire. Ils permettent même de représenter des séquences infinies, ce qui est impossible avec une list classique.
- Itérables et itérateurs
En Python, tout objet utilisable dans une boucle for est un itérable (Iterable). Cela inclut :
- Les types de données de collection : listes, tuples, dictionnaires, ensembles, chaînes
- Les générateurs
On peut vérifier avec isinstance() :
from collections.abc import Iterable
print(isinstance([], Iterable)) # True
print(isinstance({}, Iterable)) # True
print(isinstance("abc", Iterable)) # True
print(isinstance((x for x in range(10)), Iterable)) # True
print(isinstance(100, Iterable)) # False
Un itérateur (Iterator) est un flux de données. On peut lui appliquer next() pour obtenir l'élément suivant, jusqu'à ce que StopIteration soit levé. Les générateurs sont des itérateurs, mais les listes, dictionnaires ou chaînes ne le sont pas.
Pour transformer un itérable en itérateur, on utilise iter() :
from collections.abc import Iterator
print(isinstance(iter([]), Iterator)) # True
print(isinstance(iter("abc"), Iterator)) # True
Sous le capot, une boucle for en Python équivaut à :
# Transformation implicite
collection = [1, 2, 3, 4, 5]
it = iter(collection)
while True:
try:
element = next(it)
# traitement
except StopIteration:
break
- Le module itertools
Le module standard itertools propose des fonctions puissantes pour manipuler les itérateurs. Voici un exemple avec islice :
from itertools import islice
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
gen_fibo = fibonacci()
dix_premiers = list(islice(gen_fibo, 10))
print(dix_premiers)
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
islice(iterable, stop) crée un itérateur qui produit les stop premiers éléments de iterable, sans consommer toute la séquence en mémoire. Cela fonctionne aussi avec des paramètres de début et de pas : islice(iterable, start, stop, step).