La fonction yield

24 Apr 2019 24 Apr 2019 13118 vues ESSADDOUKI Mostafa 5 min de lecture

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.

Définition — 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.
Définition — Fonction génératrice Une fonction génératrice est une fonction ordinaire dont le corps contient au moins un yield. Elle retourne automatiquement un objet générateur, qui produit les valeurs à la demande (lazy evaluation), sans les calculer toutes d'avance.

Syntaxe Python
def generateur():
    # ...
    yield valeur      # suspend et renvoie valeur
    # ... reprend ici à la prochaine itération

return vs yield


returnyield
Valeurs produitesUne seule valeurUne séquence de valeurs
ExécutionTermine la fonctionSuspend et reprend
État conservé❌ Perdu✅ Conservé
MémoireToutes les valeurs en mémoireUne valeur à la fois
Type retournéValeur directeObjet generator
Cas d'usageRésultat unique ou liste finieSé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)
Sortie
0
1
2
3
4
5

Voici ce qui se passe à chaque itération de la boucle for :

  1. La boucle appelle next() sur le générateur.
  2. Le générateur s'exécute jusqu'au prochain yield.
  3. Il renvoie la valeur et se met en pause.
  4. À l'itération suivante, il reprend exactement après le yield.
  5. Quand la fonction se termine, une exception StopIteration est 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)
Sortie
2
6
24
120
Astuce — Utiliser 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))   # 24

Exemple — 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, 4
Attention Ne jamais appeler list() 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

Avantages
  • É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.
Inconvénients
  • 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'usagePourquoi yield ?Exemple
Lecture de fichiers volumineuxLire ligne par ligne sans tout chargeryield ligne dans une boucle
Flux de données / API paginéeRécupérer les données au fur et à mesureGénérateur de pages de résultats
Suites mathématiques infiniesFibonacci, nombres premiers, …yield dans while True
Pipeline de traitementChaîner des transformations sans listes intermédiairesitertools + générateurs
Saisies interactivesProduire des valeurs selon les entrées utilisateuryield 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 13
Sortie
0 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.