ProgrammationDéveloppeur Backend

Qu'est-ce que les itérateurs, les générateurs et la syntaxe yield en Python, comment sont-ils liés et pourquoi yield est-il important pour le traitement efficace de grandes données ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Les itérateurs et les générateurs sont à la base du traitement efficace des séquences en Python. Historiquement, Python a cherché à simplifier le travail avec des flux de données et à éviter de stocker de grandes collections en mémoire. Au départ, le support des itérateurs a été introduit via les protocoles __iter__ et __next__, puis les générateurs ont permis de créer de simples itérateurs à l'aide de constructions basées sur yield.

Problème : il est souvent nécessaire de traiter de grandes quantités de données (par exemple, le streaming à partir d'un fichier ou d'une base de données), ce qui n'est pas pratique ni efficace si l'on charge tout en mémoire à la fois. Les fonctions ordinaires renvoient tous les résultats à la fois, et créer ses propres itérateurs via des classes est souvent trop encombrant pour des cas simples.

Solution : le mécanisme yield permet d'organiser une génération de données "paresseuse". La fonction génératrice ne retourne pas une liste ou une autre collection, mais renvoie un objet générateur — un itérateur qui calcule les valeurs au fur et à mesure de la demande.

Exemple de code :

# Générateur simple def countdown(n): while n > 0: yield n n -= 1 for i in countdown(3): print(i) # 3, 2, 1

Caractéristiques clés :

  • Économie de mémoire : les données sont créées à la demande, et non à l'avance.
  • Simplicité de la syntaxe : yield réalise un itérateur complet en quelques lignes de code.
  • Contrôle de l'exécution : les générateurs conservent l'état entre les appels.

Questions pièges.

Peut-on utiliser return et yield dans une même fonction ?

Oui, mais return dans un générateur termine l'itération (lève StopIteration), tandis que yield peut être utilisé autant de fois que nécessaire.

def example(): yield 1 return # StopIteration

Pourquoi un générateur ne peut-il pas être "redémarré" après avoir terminé ?

Un générateur, après avoir épuisé les itérations (StopIteration), ne peut pas être redémarré, il doit être créé à nouveau.

gen = countdown(2) list(gen) # [2, 1] list(gen) # [] (le générateur est déjà épuisé)

Quelle est la différence entre un générateur et un itérateur ?

Un générateur est un cas particulier d'itérateur ; tout objet avec les méthodes iter et next est un itérateur, mais un générateur est créé via une fonction avec yield.

Erreurs typiques et anti-patrons

  • Ils oublient que les générateurs sont "jetables" : après épuisement, ils ne peuvent pas être réutilisés.
  • Ils confondent le fonctionnement de return et yield à l'intérieur des fonctions génératrices.
  • Ils stockent les résultats de la génération dans une liste – perdent le sens des calculs paresseux.

Exemple de la vie

Cas négatif

Un développeur a écrit une fonction qui charge un million de lignes d'un fichier en mémoire via list(fd) pour analyse. Cela a conduit à un débordement de mémoire sur le serveur.

Avantages :

  • Accès rapide à toutes les lignes.

Inconvénients :

  • Haute consommation de mémoire.
  • Possibilité de pannes en raison d'un manque de mémoire.

Cas positif

Utilisation d'un générateur pour lire un fichier ligne par ligne et analyser les données à la volée avec yield.

Avantages :

  • Utilisation minimale de la mémoire.
  • Peut travailler avec des fichiers de n'importe quelle taille.

Inconvénients :

  • Impossible d'accéder aux données déjà lues sans stockage supplémentaire.