Les Python Ge­ne­ra­tors cons­ti­tuent une forme spéciale de fonctions dans le langage Python ; ils génèrent des valeurs à chaque étape pour vous permettre de tra­vail­ler de façon efficace sur le plan de la mémoire.

Python Ge­ne­ra­tors : de quoi s’agit-il exac­te­ment ?

Les Python Ge­ne­ra­tors cor­res­pon­dent à des fonctions spéciales et renvoient un Python Iterator. Pour créer des Python Ge­ne­ra­tors, vous pouvez procéder de la même manière que pour une dé­fi­ni­tion de fonction normale. La dif­fé­rence se cache dans les détails : au lieu d’une dé­cla­ra­tion return, les Python Ge­ne­ra­tors renvoient une dé­cla­ra­tion yield. Comme les fonctions Iterator, les fonctions Generator im­plé­men­tent elles aussi une fonction next().

Note

Les Python Ge­ne­ra­tors comptent parmi les concepts avancés de la pro­gram­ma­tion avec Python. Si vous n’apprenez plus rien dans les tutoriels Python conçus pour les débutants, nous vous con­seil­lons de vous in­té­res­ser aux articles suivants :

Le mot-clé « yield »

Si vous avez déjà une certaine ex­pé­rience avec d’autres langages de pro­gram­ma­tion ou avec Python, la dé­cla­ra­tion return doit déjà vous être familière. Cette dernière est utilisée pour trans­mettre les valeurs calculées par les fonctions à l’instance appelante dans le code du programme. Dès que la dé­cla­ra­tion return d’une fonction est atteinte, elle est aban­don­née et son exécution est in­ter­rom­pue. La fonction peut être appelée de nouveau, le cas échéant.

Avec yield, la procédure est dif­fé­rente : dans les Python Ge­ne­ra­tors, ce mot-clé remplace la dé­cla­ra­tion return. Si vous appelez votre Python Generator, il renvoie donc la valeur transmise à la commande yield. Après cette opération, le Python Generator n’est pas abandonné, mais seulement in­ter­rompu. L’état de la fonction Generator peut alors être rapproché du statut « En­re­gis­tré ». Ainsi, en appelant une nouvelle fois votre Python Generator, vous le retrouvez à la position en­re­gis­trée.

Domaines d’uti­li­sa­tion des Python Ge­ne­ra­tors

Comme le fonc­tion­ne­ment des Python Ge­ne­ra­tors est basé sur le principe d’une « lazy eva­lua­tion » (lit­té­ra­le­ment « éva­lua­tion pa­res­seuse ») et qu’ils n’évaluent les valeurs qu’en cas d’absolue nécessité, les fonctions Generator se prêtent par­ti­cu­liè­re­ment bien au travail avec d’im­por­tants volumes de données.

Une fonction normale com­men­ce­rait par charger l’ensemble du contenu de votre fichier dans une variable, et donc dans votre mémoire. Si vous tra­vail­lez avec d’im­por­tants volumes de données, votre mémoire locale pourrait ne pas suffire et le processus se solderait donc par le message « Me­mo­ryEr­ror ». Les Python Ge­ne­ra­tors vous per­met­tent d’éviter fa­ci­le­ment les problèmes de ce type, car ils lisent votre fichier ligne par ligne. Le mot-clé yield vous renvoie la valeur né­ces­saire au moment où vous en avez besoin, puis in­ter­rompt l’exécution de la fonction jusqu’à son prochain appel pour une autre ligne du fichier.

Conseil

Nom­breuses sont les ap­pli­ca­tions Web qui né­ces­si­tent le trai­te­ment d’im­por­tants volumes de données. Python est également adapté au travail sur des projets Web. Avec l’outil Deploy Now, vous pouvez accélérer la création de vos projets Web en profitant de l’en­vi­ron­ne­ment de création et de dé­ploie­ment au­to­ma­tiques de GitHub.

Si les Python Ge­ne­ra­tors fa­ci­li­tent énor­mé­ment le trai­te­ment d’im­por­tants volumes de données, ils per­met­tent également le travail avec des séries infinies. Étant donné que votre mémoire locale est forcément limitée, les Python Ge­ne­ra­tors sont votre seule option pour créer des listes infinies ou d’autres éléments avec Python.

Python Ge­ne­ra­tors : ex­trac­tion de fichiers CSV

Comme nous l’avons déjà dit, les Python Ge­ne­ra­tors sont par­ti­cu­liè­re­ment adaptés au travail avec d’im­por­tants volumes de données. Le programme suivant vous permet d’extraire un fichier CSV ligne par ligne de façon efficace sur le plan de la mémoire :

import csv
def csv_lire(nomdufichier):
 with open(nomdufichier, ‘r’) en tant que fichier:
  tmp = csv.reader(fichier)
  for ligne in tmp:
   yield ligne
for ligne in csv_lire(‘test.csv’):
 print(ligne)
Python

Dans l’exemple de code, nous importons tout d’abord le module « csv » pour accéder aux fonctions de trai­te­ment des fichiers CSV proposées par Python ; vient ensuite la dé­fi­ni­tion d’un Python Generator portant le nom « csv_lire ». Comme toute dé­fi­ni­tion de fonction, elle commence par le mot-clé « def ». Une fois le fichier ouvert, la boucle for en Python effectue une itération ligne par ligne dans le fichier. Chaque ligne est ensuite renvoyée avec le mot-clé yield.

En dehors de la fonction Generator, les lignes renvoyées par le Python Generator doivent s’afficher l’une après l’autre sur la console. Pour ce faire, il convient d’utiliser la fonction print dans Python.

Python Ge­ne­ra­tors : créer des struc­tures de données infinies

En toute logique, aucune structure de données infinie ne peut être en­re­gis­trée lo­ca­le­ment sur votre or­di­na­teur. Les struc­tures de données infinies sont toutefois es­sen­tielles au fonc­tion­ne­ment de certaines ap­pli­ca­tions. Les fonctions Generator sont également utiles à cet égard, car elles traitent tous les éléments les uns à la suite des autres, sans qu’ils en­combrent la mémoire. Vous trouverez ci-dessous un exemple de série infinie de nombres entiers naturels en code Python :

def nombre_naturel():
 n = 0
 while True:
  yield n
  n += 1
for nombre in nombre_naturel():
 print(nombre)
Python

Un Python Generator nommé « nombre_naturel » est d’abord défini ; il détermine la valeur de départ de la variable « n ». Ensuite, une boucle while en Python est lancée et s’exécute à l’infini. Avec « yield », la dernière valeur de la variable est renvoyée et l’exécution de la fonction Generator est in­ter­rom­pue. En cas de nouvel appel de la fonction, le nombre émis au­pa­ra­vant est augmenté de 1 et le Python Generator s’exécute à nouveau, jusqu’à ce que l’in­ter­pré­teur rencontre le mot-clé yield. Les nombres produits par le Python Generator s’affichent dans la boucle for, sous la fonction Generator. Si vous n’in­ter­rom­pez pas le programme ma­nuel­le­ment, il s’exécute à l’infini.

Python Ge­ne­ra­tors : abré­via­tions

Les listes en com­pré­hen­sion per­met­tent de créer des listes avec Python en une seule ligne de code. Il existe également une abré­via­tion pour les Python Ge­ne­ra­tors. Prenons l’exemple d’un Python Generator qui passe en revue les chiffres de 0 à 9 en aug­men­tant de 1 à chaque fois. Ce Python Generator est semblable à celui que nous avons utilisé pour générer la suite infinie de nombres entiers naturels.

def nombre_naturel():
	n = 0
	while n <= 9:
		yield n
		n+=1
Python

Pour écrire ce Python Generator en une seule ligne de code, il vous suffit d’utiliser une dé­cla­ra­tion for entre pa­ren­thèses, comme dans l’exemple suivant :

increment_generator = (n + 1 for n in range(10))
Python

Si vous émettez à présent ce Python Generator, vous obtenez le résultat suivant :

<generator object <genexpr> at 0x0000020CC5A2D6C8>

Il vous indique alors l’em­pla­ce­ment de l’objet du Python Generator que vous avez créé dans votre mémoire. Pour accéder au résultat obtenu par votre Python Generator, vous pouvez utiliser la fonction « next() » :

print(next(increment_generator))
print(next(increment_generator))
print(next(increment_generator))
Python

Cette section de code renvoie le résultat suivant, avec les chiffres de 0 à 2 chaque fois augmentés de 1 :

1
2
3

Python Ge­ne­ra­tors ou listes en com­pré­hen­sion ?

L’abré­via­tion proposée pour les Python Ge­ne­ra­tors est très proche de celle des listes en com­pré­hen­sion. La seule dif­fé­rence visible réside dans leur en­ca­dre­ment : si des crochets sont utilisés pour les listes en com­pré­hen­sion, ce sont des pa­ren­thèses qui vous per­met­tent de créer des Python Ge­ne­ra­tors. Au sein même des abré­via­tions, la dif­fé­rence est toutefois bien plus im­por­tante : Les Python Ge­ne­ra­tors utilisent beaucoup moins de mémoire que les listes en com­pré­hen­sion.

import sys
increment_liste = [n + 1 for n in range(100)]
increment_generator = (n + 1 for n in range(100))
print(sys.getsizeof(increment_liste))
print(sys.getsizeof(increment_generator))
Python

Le programme ci-dessus permet de dé­ter­mi­ner les besoins en mémoire d’une liste en com­pré­hen­sion et de son équi­valent sous forme de Python Generator.

912
120

Là où la liste nécessite 912 octets de mémoire, le Python Generator peut lui se contenter de 120 octets. Plus le volume de données à traiter augmente et plus cette dif­fé­rence gagne en im­por­tance.

Aller au menu principal