Le mot-clé yield et les générateurs
En Python, la plupart des fonctions calculent un résultat et le retournent en une seule fois avec return. Mais que faire lorsqu'on veut produire une séquence de valeurs sans les stocker toutes en mémoire ? C'est le rôle de yield.
yield yield est un mot-clé qui suspend l'exécution d'une fonction et renvoie une valeur à l'appelant, tout en conservant l'état complet de la fonction (variables locales, position dans le code). À la prochaine itération, l'exécution reprend exactement là où elle s'était arrêtée.yield. Elle retourne automatiquement un objet générateur, qui produit les valeurs à la demande (lazy evaluation), sans les calculer toutes d'avance.def generateur():
# ...
yield valeur # suspend et renvoie valeur
# ... reprend ici à la prochaine itérationreturn vs yield
return | yield | |
|---|---|---|
| Valeurs produites | Une seule valeur | Une séquence de valeurs |
| Exécution | Termine la fonction | Suspend et reprend |
| État conservé | ❌ Perdu | ✅ Conservé |
| Mémoire | Toutes les valeurs en mémoire | Une valeur à la fois |
| Type retourné | Valeur directe | Objet generator |
| Cas d'usage | Résultat unique ou liste finie | Séquences longues / infinies |
Exemples
Exemple — Générateur simple de 0 à n-1
def generateur(n):
for i in range(n):
yield i # suspend et renvoie i à chaque itération
for valeur in generateur(6):
print(valeur)0 1 2 3 4 5
Voici ce qui se passe à chaque itération de la boucle for :
- La boucle appelle
next()sur le générateur. - Le générateur s'exécute jusqu'au prochain
yield. - Il renvoie la valeur et se met en pause.
- À l'itération suivante, il reprend exactement après le
yield. - Quand la fonction se termine, une exception
StopIterationest levée automatiquement.
Exemple — Générateur de factorielles (2 → n)
def factorielles(n):
f = 1
for i in range(2, n):
f *= i
yield f # renvoie la factorielle courante
for val in factorielles(6):
print(val)2 6 24 120
next() manuellement Un générateur peut aussi être parcouru manuellement avec next(), ce qui donne un contrôle total sur la progression :gen = factorielles(6)
print(next(gen)) # 2
print(next(gen)) # 6
print(next(gen)) # 24Exemple — Générateur infini
Contrairement aux listes, un générateur peut produire une séquence théoriquement infinie, car il ne calcule qu'une valeur à la fois.
def entiers_naturels():
n = 0
while True: # boucle infinie, mais sans risque mémoire
yield n
n += 1
gen = entiers_naturels()
for _ in range(5):
print(next(gen)) # 0, 1, 2, 3, 4list() ou sum() directement sur un générateur infini — cela bloquerait le programme indéfiniment. Utilisez toujours une condition d'arrêt (break, itertools.islice, etc.).Avantages et inconvénients
- Économie mémoire : seule une valeur est en mémoire à la fois. Idéal pour traiter des fichiers volumineux, des flux de données ou des séquences infinies.
- Performance : l'état local est conservé entre deux appels, évitant de recalculer depuis le début à chaque itération.
- Lisibilité : le code d'un générateur est souvent plus clair qu'une classe implémentant manuellement le protocole itérateur (
__iter__/__next__). - Composition : les générateurs peuvent être chaînés et combinés facilement avec
itertools.
- Usage unique : un générateur ne peut être parcouru qu'une seule fois. Une fois épuisé, il faut en créer un nouveau.
- Débogage difficile : le flux d'exécution suspendu/repris peut être difficile à suivre, surtout avec plusieurs
yield. - Pas d'accès aléatoire : contrairement à une liste, on ne peut pas accéder directement à
gen[i].
Applications pratiques
Les générateurs sont particulièrement adaptés dans les contextes suivants :
| Cas d'usage | Pourquoi yield ? | Exemple |
|---|---|---|
| Lecture de fichiers volumineux | Lire ligne par ligne sans tout charger | yield ligne dans une boucle |
| Flux de données / API paginée | Récupérer les données au fur et à mesure | Générateur de pages de résultats |
| Suites mathématiques infinies | Fibonacci, nombres premiers, … | yield dans while True |
| Pipeline de traitement | Chaîner des transformations sans listes intermédiaires | itertools + générateurs |
| Saisies interactives | Produire des valeurs selon les entrées utilisateur | yield input(...) |
Exemple — Lire un grand fichier ligne par ligne
def lire_lignes(chemin):
with open(chemin, 'r', encoding='utf-8') as f:
for ligne in f:
yield ligne.strip() # une ligne à la fois, sans tout charger
for ligne in lire_lignes("donnees.txt"):
print(ligne)Exemple — Suite de Fibonacci infinie
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
gen = fibonacci()
for _ in range(8):
print(next(gen), end=" ") # 0 1 1 2 3 5 8 130 1 1 2 3 5 8 13
Discussion (0)
Soyez le premier à laisser un commentaire !
Laisser un commentaire
Votre commentaire sera visible après modération.