Les générateurs en Python sont un mécanisme puissant pour travailler efficacement avec des séquences de données. Ils permettent de créer des objets itérables qui calculent les valeurs à la demande, économisant considérablement la mémoire système. Le mot-clé yield est au cœur des générateurs et se distingue radicalement du traditionnel return.
Fonctionnement de base des générateurs Python
Les générateurs sont des fonctions spéciales qui retournent des itérateurs plutôt que des valeurs concrètes. La différence principale réside dans l'utilisation du mot-clé yield, qui sauvegarde l'état de la fonction entre les appels.
À chaque appel d'une fonction génératrice, l'exécution reprend là où la dernière instruction yield s'est arrêtée. Cela fait des générateurs la solution idéale pour traiter de grands flux de données sans avoir à tout charger en mémoire.
def simple_generator():
yield 1
yield 2
yield 3
gen = simple_generator()
for value in gen:
print(value)
Résultat de l'exécution :
1
2
3
Mécanisme du mot-clé yield
Le mot-clé yield fonctionne sur le principe de l'évaluation paresseuse (lazy evaluation) :
- Au premier appel de
next(), l'exécution commence au début de la fonction jusqu'au premieryield - Après avoir retourné la valeur, la fonction "gèle" son état
- L'appel suivant de
next()reprend l'exécution là où elle s'était arrêtée - Le processus continue jusqu'à la fin de la fonction
def counter():
print("Début du générateur")
yield 1
print("Après le premier yield")
yield 2
print("Après le deuxième yield")
gen = counter()
next(gen) # Affiche : Début du générateur
next(gen) # Affiche : Après le premier yield
Avantages des générateurs par rapport aux listes
Utiliser des générateurs plutôt que des listes offre des avantages considérables pour le traitement de gros volumes de données :
Économie de mémoire : Les générateurs ne stockent pas toutes les valeurs en même temps, ils les créent au fur et à mesure des besoins.
# Liste - consomme beaucoup de mémoire
squares_list = [x * x for x in range(1000000)]
# Générateur - consommation mémoire minimale
squares_gen = (x * x for x in range(1000000))
Performance : Les générateurs commencent à retourner des valeurs instantanément, sans attendre la création complète de la séquence.
Création de séquences infinies
Les générateurs permettent de créer des séquences infinies sans risque de saturation mémoire :
def infinite_counter(start=0):
while True:
yield start
start += 1
counter = infinite_counter()
print(next(counter)) # 0
print(next(counter)) # 1
print(next(counter)) # 2
Méthodes de travail avec les générateurs
Les générateurs prennent en charge plusieurs méthodes spécialisées :
next() — obtention de la valeur suivante du générateur
send(value) — envoi d'une valeur à l'intérieur du générateur
throw() — déclenchement d'une exception dans le générateur
close() — arrêt forcé du générateur
def interactive_generator():
value = yield "Début"
while True:
value = yield f"Reçu : {value}"
gen = interactive_generator()
print(next(gen)) # Début
print(gen.send(42)) # Reçu : 42
print(gen.send("Bonjour")) # Reçu : Bonjour
Gestion des exceptions dans les générateurs
Les générateurs supportent un mécanisme de gestion des exceptions, y compris l'exception spéciale GeneratorExit :
def example_generator():
try:
yield 1
yield 2
except GeneratorExit:
print("Générateur fermé !")
gen = example_generator()
print(next(gen)) # 1
gen.close() # Affiche : Générateur fermé !