Traduction en français

Bonjour à toi, aspirant programmeur Python !aujourd'hui, nous allons entreprendre un voyage passionnant dans le monde des générateurs Python. Ne t'inquiète pas si tu ne les as jamais entendus avant – nous commencerons desde le début et nous nous frayons un chemin. À la fin de ce tutoriel, tu créeras des générateurs comme un pro !

Python - Generators

Qu'est-ce qu'un générateur Python ?

Imagine que tu lis un livre vraiment long. Au lieu de photocopier tout le livre en une fois (ce qui serait un gaspillage d'encre et de papier !), tu pourrais lire une page à la fois. C'est un peu comment les générateurs fonctionnent en Python.

Les générateurs sont un type spécial de fonction qui nous permet de générer une séquence de valeurs au fil du temps, plutôt que de les calculer toutes en une fois et de les stocker en mémoire. Ils sont comme des usines magiques qui produisent des valeurs à la demande.

Pourquoi utiliser des générateurs ?

  1. Efficacité mémoire : Les générateurs sont compatibles avec la mémoire. Ils ne stockent pas toutes les valeurs en mémoire en une fois.
  2. Performance : Ils peuvent améliorer la performance de ton code, surtout lorsqu'il s'agit de traiter de grands ensembles de données.
  3. Simplicité : Les générateurs peuvent rendre ton code plus propre et plus lisible.

Faisons un plongeon et voyons comment ils fonctionnent !

Création de générateurs

Il y a deux principales méthodes pour créer des générateurs en Python :

  1. Utiliser une fonction générateur
  2. Utiliser une expression générateur

Fonctions générateurs

Une fonction générateur ressemble à une fonction normale, mais au lieu d'utiliser le mot-clé return, elle utilise yield. Voici un exemple simple :

def compte_jusqua(n):
    i = 1
    while i <= n:
        yield i
        i += 1

# Utilisation de notre générateur
for nombre in compte_jusqua(5):
    print(nombre)

Sortie :

1
2
3
4
5

Dans cet exemple, compte_jusqua est une fonction générateur. Chaque fois qu'elle produit une valeur, elle met en pause son exécution et se souvient de son état. La prochaine fois qu'elle est appelée, elle reprend là où elle s'est arrêtée.

Expressions générateurs

Les expressions générateurs sont comme les listes par compréhension, mais avec des parenthèses au lieu de crochets. Ils sont une manière compacte de créer des générateurs. Voici un exemple :

# Expression générateur
carrés = (x**2 for x in range(5))

# Utilisation de notre générateur
for carré in carrés:
    print(carré)

Sortie :

0
1
4
9
16

Cette expression générateur crée une séquence de nombres carrés à la volée, sans les stocker tous en mémoire en une fois.

Gestion des exceptions dans les générateurs

Les générateurs peuvent également gérer les exceptions, ce qui est assez cool ! Voici un exemple :

def div_générateur(a, b):
    try:
        résultat = a / b
        yield résultat
    except ZeroDivisionError:
        yield "Impossible de diviser par zéro !"

# Utilisation de notre générateur
g = div_générateur(10, 2)
print(next(g))  # Affiche : 5.0

g = div_générateur(10, 0)
print(next(g))  # Affiche : Impossible de diviser par zéro !

Dans cet exemple, notre générateur gère avec grâce le cas où nous essayons de diviser par zéro.

Fonction normale vs Fonction générateur

Comparons une fonction normale avec une fonction générateur pour voir la différence :

# Fonction normale
def get_carrés(n):
    carrés = []
    for i in range(n):
        carrés.append(i**2)
    return carrés

# Fonction générateur
def gen_carrés(n):
    for i in range(n):
        yield i**2

# Utilisation de la fonction normale
print(get_carrés(5))  # Affiche : [0, 1, 4, 9, 16]

# Utilisation de la fonction générateur
for carré in gen_carrés(5):
    print(carré)  # Affiche chaque carré sur une nouvelle ligne

Les principales différences sont :

  1. Utilisation de la mémoire : La fonction normale crée et stocke toutes les valeurs en une fois, tandis que le générateur les produit une à la fois.
  2. Syntaxe : La fonction normale utilise return, tandis que le générateur utilise yield.
  3. Itération : Le générateur peut être itéré directement, tandis que le résultat de la fonction normale doit être stocké dans une variable primero.

Générateurs asynchrones

Python 3.6 a introduit les générateurs asynchrones, qui sont comme les générateurs normaux mais pour la programmation asynchrone. Ils utilisent async def et yield :

import asyncio

async def async_gen():
    for i in range(3):
        await asyncio.sleep(1)
        yield i

async def main():
    async for item in async_gen():
        print(item)

asyncio.run(main())

Cet exemple simule une opération asynchrone qui produit des valeurs au fil du temps.

Méthodes des générateurs

Les générateurs ont quelques méthodes spéciales qui peuvent être assez utiles. Voici un tableau des plus courantes :

Méthode Description
next() Récupère l'élément suivant du générateur
send() Envoie une valeur dans le générateur
throw() Lance une exception à l'intérieur du générateur
close() Ferme le générateur

Voici un exemple rapide de l'utilisation de send() :

def echo_générateur():
    while True:
        reçu = yield
        print(f"Reçu : {reçu}")

g = echo_générateur()
next(g)  # Prime le générateur
g.send("Bonjour")  # Affiche : Reçu : Bonjour
g.send("Monde")  # Affiche : Reçu : Monde

Et c'est tout ! Tu viens de faire tes premiers pas dans le merveilleux monde des générateurs Python. souviens-toi, la pratique fait le maître, donc n'hésite pas à expérimenter avec ces concepts. Avant que tu ne t'en rendes compte, tu utiliseras des générateurs pour résoudre tous sorts de problèmes intéressants dans tes programmes Python. Bon codage !

Credits: Image by storyset