Les générateurs sont des objets itérables spéciaux en Python qui permettent de créer des séquences "à la volée", sans occuper de mémoire pour toute la collection en même temps. Ils sont implémentés à l'aide de fonctions avec le mot-clé yield ou d'expressions génératrices ((expr for ... in ...)). C'est pratique lors du traitement de grandes quantités de données ou de flux potentiellement infinis.
Différences clés par rapport aux expressions de liste :
[x for x in range(10)]) créent immédiatement toute la liste en mémoire.(x for x in range(10))) créent les éléments un par un, consommant beaucoup moins de mémoire.Quand utiliser des générateurs :
# Fonction génératrice def counter(n): for i in range(n): yield i for number in counter(5): print(number)
"Quelle est la différence entre l'utilisation d'une fonction avec yield et une fonction ordinaire qui retourne une liste ? Donnez un exemple."
Réponse :
Une fonction ordinaire calcule et retourne immédiatement une liste, occupant ainsi de la mémoire pour tous ses éléments. Une fonction avec yield retourne un générateur, qui produit les éléments un par un sans charger toute la séquence en mémoire à la fois.
def make_list(n): return [i for i in range(n)] # Retourne immédiatement une liste, utilise beaucoup de mémoire def make_generator(n): for i in range(n): yield i # Produira un élément à la fois
Histoire
Dans un projet d'analyse de grands journaux, des expressions de liste ont été utilisées pour extraire des lignes contenant des erreurs :
error_lines = [line for line in open('biglog.txt') if 'ERROR' in line]
Le fichier dépassait 2 Go et l'application a échoué avec OOM (Out of Memory). Il fallait utiliser un générateur :
error_lines = (line for line in open('biglog.txt') if 'ERROR' in line)
Histoire
Un employé voulait analyser une courte liste, écrit une fonction avec yield, mais a oublié que ce qui est retourné est un générateur et non une liste :
result = my_generator_function() # result — générateur, pas liste if len(result) > 5: # TypeError: objet de type 'generator' n'a pas de len()
Correction : envelopper le résultat dans list().
Histoire
Ils ont essayé d'itérer sur un générateur plusieurs fois :
numbers = (i for i in range(5)) for n in numbers: pass # épuisé le générateur for n in numbers: print(n) # ne produit rien
Un générateur est à usage unique. Il faut en créer un nouveau pour une réutilisation.