Python Generators : pour une programmation efficace sur le plan de la mémoire

Les Python Generators constituent une forme spéciale de fonctions dans le langage Python ; ils génèrent des valeurs à chaque étape pour vous permettre de travailler de façon efficace sur le plan de la mémoire.

Python Generators : de quoi s’agit-il exactement ?

Les Python Generators correspondent à des fonctions spéciales et renvoient un Python Iterator. Pour créer des Python Generators, vous pouvez procéder de la même manière que pour une définition de fonction normale. La différence se cache dans les détails : au lieu d’une déclaration return, les Python Generators renvoient une déclaration yield. Comme les fonctions Iterator, les fonctions Generator implémentent elles aussi une fonction next().

Note

Les Python Generators comptent parmi les concepts avancés de la programmation avec Python. Si vous n’apprenez plus rien dans les tutoriels Python conçus pour les débutants, nous vous conseillons de vous intéresser aux articles suivants :

Le mot-clé « yield »

Si vous avez déjà une certaine expérience avec d’autres langages de programmation ou avec Python, la déclaration return doit déjà vous être familière. Cette dernière est utilisée pour transmettre les valeurs calculées par les fonctions à l’instance appelante dans le code du programme. Dès que la déclaration return d’une fonction est atteinte, elle est abandonnée et son exécution est interrompue. La fonction peut être appelée de nouveau, le cas échéant.

Avec yield, la procédure est différente : dans les Python Generators, ce mot-clé remplace la déclaration 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 interrompu. L’état de la fonction Generator peut alors être rapproché du statut « Enregistré ». Ainsi, en appelant une nouvelle fois votre Python Generator, vous le retrouvez à la position enregistrée.

Domaines d’utilisation des Python Generators

Comme le fonctionnement des Python Generators est basé sur le principe d’une « lazy evaluation » (littéralement « évaluation paresseuse ») et qu’ils n’évaluent les valeurs qu’en cas d’absolue nécessité, les fonctions Generator se prêtent particulièrement bien au travail avec d’importants volumes de données.

Une fonction normale commencerait par charger l’ensemble du contenu de votre fichier dans une variable, et donc dans votre mémoire. Si vous travaillez avec d’importants volumes de données, votre mémoire locale pourrait ne pas suffire et le processus se solderait donc par le message « MemoryError ». Les Python Generators vous permettent d’éviter facilement les problèmes de ce type, car ils lisent votre fichier ligne par ligne. Le mot-clé yield vous renvoie la valeur nécessaire au moment où vous en avez besoin, puis interrompt l’exécution de la fonction jusqu’à son prochain appel pour une autre ligne du fichier.

Conseil

Nombreuses sont les applications Web qui nécessitent le traitement d’importants 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’environnement de création et de déploiement automatiques de GitHub.

Si les Python Generators facilitent énormément le traitement d’importants volumes de données, ils permettent également le travail avec des séries infinies. Étant donné que votre mémoire locale est forcément limitée, les Python Generators sont votre seule option pour créer des listes infinies ou d’autres éléments avec Python.

Python Generators : extraction de fichiers CSV

Comme nous l’avons déjà dit, les Python Generators sont particulièrement adaptés au travail avec d’importants 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 traitement des fichiers CSV proposées par Python ; vient ensuite la définition d’un Python Generator portant le nom « csv_lire ». Comme toute définition 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 Generators : créer des structures de données infinies

En toute logique, aucune structure de données infinie ne peut être enregistrée localement sur votre ordinateur. Les structures de données infinies sont toutefois essentielles au fonctionnement de certaines applications. Les fonctions Generator sont également utiles à cet égard, car elles traitent tous les éléments les uns à la suite des autres, sans qu’ils encombrent 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 interrompue. En cas de nouvel appel de la fonction, le nombre émis auparavant est augmenté de 1 et le Python Generator s’exécute à nouveau, jusqu’à ce que l’interpré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’interrompez pas le programme manuellement, il s’exécute à l’infini.

Python Generators : abréviations

Les listes en compréhension permettent de créer des listes avec Python en une seule ligne de code. Il existe également une abréviation pour les Python Generators. Prenons l’exemple d’un Python Generator qui passe en revue les chiffres de 0 à 9 en augmentant 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éclaration for entre parenthè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’emplacement 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 Generators ou listes en compréhension ?

L’abréviation proposée pour les Python Generators est très proche de celle des listes en compréhension. La seule différence visible réside dans leur encadrement : si des crochets sont utilisés pour les listes en compréhension, ce sont des parenthèses qui vous permettent de créer des Python Generators. Au sein même des abréviations, la différence est toutefois bien plus importante : Les Python Generators utilisent beaucoup moins de mémoire que les listes en compréhension.

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éterminer les besoins en mémoire d’une liste en compréhension et de son équivalent 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 différence gagne en importance.