Python >> Tutoriel Python >  >> Python

Que sont les générateurs en Python ?

Avez-vous déjà traité des ensembles de données si volumineux que la mémoire de votre ordinateur ne pouvait pas les gérer ? Vous êtes-vous déjà demandé s'il pouvait y avoir un moyen d'interrompre une fonction au milieu avant de la reprendre ? C'est là que les générateurs Python entrent en scène.

Les générateurs Python sont un moyen de créer des itérateurs personnalisés. Vous pouvez en savoir plus sur les itérateurs ici. Avant de continuer, si vous n'êtes pas familier avec la terminologie Python, consultez nos articles sur les termes Python pour les débutants et d'autres termes Python. Et si vous n'êtes pas à l'aise avec les opérations sur les structures de données en Python, vous pouvez essayer notre cours sur les algorithmes intégrés en Python.

Vous pouvez boucler sur des objets générateurs comme vous le feriez avec une liste. Mais contrairement aux listes, les générateurs ne stockent pas leur contenu en mémoire. Un cas d'utilisation répandu est lorsque vous devez gérer des fichiers plus volumineux que la mémoire de votre machine ne peut gérer, par ex. un grand ensemble de données. Essayer d'ouvrir un tel fichier entraînerait un MemoryError .

En utilisant un générateur Python, vous pouvez éviter un tel problème. Mais attendez! Comment définissez-vous les générateurs Python ?

Comment définir des générateurs en Python

Un générateur Python est très similaire à une fonction Python normale, mais nous le terminons par yield au lieu du mot-clé return. Écrivons un exemple rapide en utilisant un for boucle.

def regular_function(x):
    for i in range(x):
        return i*5

Une fois exécuté en tant que regular_function(10) , cette fonction régulière renverra 0, car l'exécution s'arrête après la première itération.

Cependant, écrivons-le un peu différemment :

def generator(x):
    for i in range(x):
        yield i*5

Le Python yield mot-clé indique que nous avons lancé un objet générateur ; il est là pour contrôler le flux du générateur Python. Lorsque le programme l'atteint, l'exécution de la fonction est interrompue et la valeur de yield est renvoyé.

À ce stade, l'état de la fonction est enregistré et la fonction reprend son exécution chaque fois que vous appelez l'une des méthodes du générateur. Un return l'instruction arrête complètement la fonction.

Par conséquent, lorsque nous exécutons ..

generator(10)

.. on obtient :

<generator object generator at 0x00000262F8EBB190>

Ensuite, nous instancions l'objet générateur en tant que g :

>>> g = generator(10) 

Pour exécuter le générateur en Python, nous devons utiliser la méthode next(). Dans l'exemple suivant, nous incluons print instructions pour obtenir des sorties :

>>> print(next(g))
0
>>> print(next(g))
5
>>> print(next(g))
10
>>> print(next(g))
15

Alors que le next() est spécifique aux générateurs en Python, ce n'est pas le seul moyen de mettre en pause ou de terminer un for boucle.

Une autre façon de définir un générateur en Python consiste à utiliser des compréhensions de générateur. Très similaires aux compréhensions de liste, les compréhensions de générateurs peuvent être définies comme :

gen_comp = (i*5 for i in range(10))

Par rapport aux compréhensions de liste, les compréhensions de générateur ont l'avantage de ne pas construire et conserver l'intégralité de l'objet en mémoire avant l'itération. Comparons une composition de générateur avec une compréhension de liste :

list_comp = [i*5 for i in range(100000)]
gen_comp = (i*5 for i in range(10000))

Ces expressions se ressemblent beaucoup; les seules différences sont les crochets et les parenthèses. Même ainsi, ils sont en fait très différents. Voyons leur taille :

>>> import sys 
>>> list_comp
>>> print('list comprehension:', sys.getsizeof(list_comp), 'bytes')
list comprehension: 87616 bytes
>>> gen_comp 
>>> print('generator comprehension:', sys.getsizeof(gen_comp), 'bytes')
generator comprehension: 112 bytes

Dans ce cas, l'objet liste est environ 782 fois plus grand que l'objet générateur. Par conséquent, si la mémoire est un problème, vous feriez mieux d'utiliser un générateur Python.

Enfin et surtout, il n'y a pas de différence entre un générateur régulier et les compréhensions du générateur en dehors de la syntaxe. La seule différence est que les compréhensions du générateur sont des lignes simples.

Si vous devez définir une boucle infinie pour une raison quelconque, vous devrez utiliser un générateur Python. Bien que votre séquence puisse être infinie, la mémoire de votre ordinateur ne l'est certainement pas.

def infinity():
    n = 0
    while True:
        yield n*n
        n += 13

On initialise une variable n et démarrer une boucle infinie. Le mot-clé yield capturera l'état initial et imitera l'action de range(); enfin, on incrémente n par 13. Ce programme se poursuivra avec un for boucle jusqu'à ce que nous l'arrêtions manuellement.

Dans notre cas, en appelant le next() , nous pouvons itérer manuellement à plusieurs reprises, ce qui est utile pour tester le générateur afin de s'assurer qu'il produit la sortie attendue. Cela signifie-t-il que le générateur peut continuer indéfiniment ?

Comment terminer les générateurs en Python

Tout d'abord, cela peut s'arrêter naturellement. En d'autres termes, une fois que toutes les valeurs ont été évaluées, l'itération s'arrêtera et le for boucle va sortir.

Si vous utilisez next() , vous obtiendrez un StopIteration explicite exception.

Une autre façon de terminer un générateur Python est d'utiliser le close() méthode, comme suit :

>>> def generator(x):
...    for i in range(x):
...        yield i*5
>>> g = generator(10)

>>> print(next(g))
0
>>> print(next(g))
5
>>> print(g.close())
None

Le close() méthode lancera un GeneratorExit au yield valeur et arrêter l'exécution du générateur. Cela peut être pratique pour contrôler le débit d'un générateur infini.

Réflexions finales sur les générateurs en Python

Dans cet article, nous avons découvert les générateurs en Python. Nous avons découvert comment ils pourraient être utiles pour gérer les calculs gourmands en mémoire et comment ils peuvent nous fournir plus de flexibilité sur nos fonctions (par exemple lors du test d'une sortie).

Je vous encourage à explorer plus avant les exemples de cet article et à consulter la documentation Python pour plus d'informations. Nos cours Python Basics peuvent également aider les nouveaux programmeurs à acquérir une expérience pratique du codage. Aucune connaissance préalable en informatique n'est requise.

Enfin, n'oubliez pas de consulter nos autres articles sur LearnPython.com. Bon apprentissage !