Apprenez les générateurs délégués

PythonPythonBeginner
Pratiquer maintenant

This tutorial is from open-source community. Access the source code

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Dans ce laboratoire (lab), vous allez apprendre à déléguer des générateurs en Python en utilisant l'instruction yield from. Cette fonctionnalité, introduite en Python 3.3, simplifie le code qui dépend de générateurs et de coroutines.

Les générateurs sont des fonctions spéciales qui peuvent mettre en pause et reprendre leur exécution, tout en conservant leur état entre les appels. L'instruction yield from offre un moyen élégant de déléguer le contrôle à un autre générateur, améliorant ainsi la lisibilité et la maintenabilité du code.

Objectifs :

  • Comprendre le but de l'instruction yield from
  • Apprendre à utiliser yield from pour déléguer à d'autres générateurs
  • Appliquer ces connaissances pour simplifier le code basé sur les coroutines
  • Comprendre le lien avec la syntaxe moderne async/await

Fichiers avec lesquels vous allez travailler :

  • cofollow.py - Contient des fonctions utilitaires pour les coroutines
  • server.py - Contient une implémentation simple d'un serveur réseau

Comprendre l'instruction yield from

Dans cette étape, nous allons explorer l'instruction yield from en Python. Cette instruction est un outil puissant lorsqu'on travaille avec des générateurs, et elle simplifie le processus de délégation d'opérations à d'autres générateurs. À la fin de cette étape, vous comprendrez ce que yield from est, comment il fonctionne et comment il peut gérer le passage de valeurs entre différents générateurs.

Qu'est-ce que yield from ?

L'instruction yield from a été introduite en Python 3.3. Son principal but est de simplifier la délégation d'opérations aux sous-générateurs. Un sous-générateur est simplement un autre générateur auquel un générateur principal peut déléguer du travail.

Normalement, lorsque vous voulez qu'un générateur produise des valeurs issues d'un autre générateur, vous devez utiliser une boucle. Par exemple, sans yield from, vous écrirez du code comme ceci :

def delegating_generator():
    for value in subgenerator():
        yield value

Dans ce code, le delegating_generator utilise une boucle for pour itérer sur les valeurs produites par subgenerator puis produit chaque valeur une par une.

Cependant, avec l'instruction yield from, le code devient beaucoup plus simple :

def delegating_generator():
    yield from subgenerator()

Cette seule ligne de code atteint le même résultat que la boucle de l'exemple précédent. Mais yield from n'est pas seulement un raccourci. Il gère également la communication bidirectionnelle entre l'appelant et le sous-générateur. Cela signifie que toutes les valeurs envoyées au générateur déléguant sont passées directement au sous-générateur.

Exemple de base

Créons un exemple simple pour voir comment yield from fonctionne en pratique.

  1. Tout d'abord, nous devons ouvrir le fichier cofollow.py dans l'éditeur. Pour ce faire, nous allons utiliser la commande cd pour naviguer jusqu'au bon répertoire. Exécutez la commande suivante dans le terminal :
cd /home/labex/project
  1. Ensuite, nous allons ajouter deux fonctions au fichier cofollow.py. La fonction subgen est un simple générateur qui produit les nombres de 0 à 4. La fonction main_gen utilise yield from pour déléguer la génération de ces nombres à subgen puis produit la chaîne de caractères 'Done'. Ajoutez le code suivant à la fin du fichier cofollow.py :
def subgen():
    for i in range(5):
        yield i

def main_gen():
    yield from subgen()
    yield 'Done'
  1. Maintenant, testons ces fonctions. Ouvrez un interpréteur Python et exécutez le code suivant :
from cofollow import subgen, main_gen

## Test subgen directly
for x in subgen():
    print(x)

## Test main_gen that delegates to subgen
for x in main_gen():
    print(x)

Lorsque vous exécutez ce code, vous devriez voir la sortie suivante :

0
1
2
3
4

0
1
2
3
4
Done

Cette sortie montre que yield from permet à main_gen de passer toutes les valeurs générées par subgen directement à l'appelant.

Passage de valeurs avec yield from

L'une des fonctionnalités les plus puissantes de yield from est sa capacité à gérer le passage de valeurs dans les deux sens. Créons un exemple plus complexe pour démontrer cela.

  1. Ajoutez les fonctions suivantes au fichier cofollow.py :
def accumulator():
    total = 0
    while True:
        value = yield total
        if value is None:
            break
        total += value

def caller():
    acc = accumulator()
    yield from acc
    yield 'Total accumulated'

La fonction accumulator est une coroutine qui suit un total courant. Elle produit le total actuel puis attend de recevoir une nouvelle valeur. Si elle reçoit None, elle arrête la boucle. La fonction caller crée une instance de accumulator et utilise yield from pour déléguer toutes les opérations d'envoi et de réception à elle.

  1. Testez ces fonctions dans un interpréteur Python :
from cofollow import caller

c = caller()
print(next(c))  ## Start the coroutine
print(c.send(1))  ## Send value 1, get accumulated value
print(c.send(2))  ## Send value 2, get accumulated value
print(c.send(3))  ## Send value 3, get accumulated value
print(c.send(None))  ## Send None to exit the accumulator

Lorsque vous exécutez ce code, vous devriez voir la sortie suivante :

0
1
3
6
'Total accumulated'

Cette sortie montre que yield from délègue entièrement toutes les opérations d'envoi et de réception au sous-générateur jusqu'à ce qu'il soit épuisé.

Maintenant que vous comprenez les bases de yield from, nous passerons à des applications plus pratiques dans l'étape suivante.

✨ Vérifier la solution et pratiquer

Utilisation de yield from dans les coroutines

Dans cette étape, nous allons explorer comment utiliser l'instruction yield from avec les coroutines pour des applications plus pratiques. Les coroutines sont un concept puissant en Python, et comprendre comment utiliser yield from avec elles peut grandement simplifier votre code.

Coroutines et passage de messages

Les coroutines sont des fonctions spéciales qui peuvent recevoir des valeurs via l'instruction yield. Elles sont incroyablement utiles pour des tâches telles que le traitement de données et la gestion d'événements. Dans le fichier cofollow.py, il y a un décorateur consumer. Ce décorateur aide à configurer les coroutines en les faisant avancer automatiquement jusqu'au premier point yield. Cela signifie que vous n'avez pas à démarrer manuellement la coroutine ; le décorateur s'en charge pour vous.

Créons une coroutine qui reçoit des valeurs et valide leur type. Voici comment vous pouvez le faire :

  1. Tout d'abord, ouvrez le fichier cofollow.py dans l'éditeur. Vous pouvez utiliser la commande suivante dans le terminal pour naviguer jusqu'au bon répertoire :
cd /home/labex/project
  1. Ensuite, ajoutez la fonction receive suivante à la fin du fichier cofollow.py. Cette fonction est une coroutine qui recevra un message et en validera le type.
def receive(expected_type):
    """
    A coroutine that receives a message and validates its type.
    Returns the received message if it matches the expected type.
    """
    msg = yield
    assert isinstance(msg, expected_type), f'Expected type {expected_type}'
    return msg

Voici ce que fait cette fonction :

  • Elle utilise yield sans expression pour recevoir une valeur. Lorsqu'une valeur est envoyée à la coroutine, cette instruction yield la capturera.
  • Elle vérifie si la valeur reçue est du type attendu en utilisant la fonction isinstance. Si le type ne correspond pas, elle lève une AssertionError.
  • Si la vérification de type réussit, elle retourne la valeur.
  1. Maintenant, créons une coroutine qui utilise yield from avec notre fonction receive. Cette nouvelle coroutine recevra et affichera uniquement des entiers.
@consumer
def print_ints():
    """
    A coroutine that receives and prints integers only.
    Uses yield from to delegate to the receive coroutine.
    """
    while True:
        val = yield from receive(int)
        print('Got:', val)
  1. Pour tester cette coroutine, ouvrez un interpréteur Python et exécutez le code suivant :
from cofollow import print_ints

p = print_ints()
p.send(42)
p.send(13)
try:
    p.send('13')  ## This should raise an AssertionError
except AssertionError as e:
    print(f"Error: {e}")

Vous devriez voir la sortie suivante :

Got: 42
Got: 13
Error: Expected type <class 'int'>

Comprendre le fonctionnement de yield from avec les coroutines

Lorsque nous utilisons yield from receive(int) dans la coroutine print_ints, les étapes suivantes se produisent :

  1. Le contrôle est délégué à la coroutine receive. Cela signifie que la coroutine print_ints se met en pause et que la coroutine receive commence à s'exécuter.
  2. La coroutine receive utilise yield pour recevoir une valeur. Elle attend qu'une valeur lui soit envoyée.
  3. Lorsqu'une valeur est envoyée à print_ints, elle est en fait reçue par receive. L'instruction yield from s'occupe de passer la valeur de print_ints à receive.
  4. La coroutine receive valide le type de la valeur reçue. Si le type est correct, elle retourne la valeur.
  5. La valeur retournée devient le résultat de l'expression yield from dans la coroutine print_ints. Cela signifie que la variable val dans print_ints reçoit la valeur retournée par receive.

L'utilisation de yield from rend le code plus lisible que si nous devions gérer directement le rendu et la réception. Elle abstrait la complexité du passage de valeurs entre les coroutines.

Création de coroutines de vérification de type plus avancées

Étendons nos fonctions utilitaires pour gérer une validation de type plus complexe. Voici comment vous pouvez le faire :

  1. Ajoutez les fonctions suivantes au fichier cofollow.py :
def receive_dict():
    """Receive and validate a dictionary"""
    result = yield from receive(dict)
    return result

def receive_str():
    """Receive and validate a string"""
    result = yield from receive(str)
    return result

@consumer
def process_data():
    """Process different types of data using the receive utilities"""
    while True:
        print("Waiting for a string...")
        name = yield from receive_str()
        print(f"Got string: {name}")

        print("Waiting for a dictionary...")
        data = yield from receive_dict()
        print(f"Got dictionary with {len(data)} items: {data}")

        print("Processing complete for this round.")
  1. Pour tester la nouvelle coroutine, ouvrez un interpréteur Python et exécutez le code suivant :
from cofollow import process_data

proc = process_data()
proc.send("John Doe")
proc.send({"age": 30, "city": "New York"})
proc.send("Jane Smith")
try:
    proc.send(123)  ## This should raise an AssertionError
except AssertionError as e:
    print(f"Error: {e}")

Vous devriez voir une sortie comme celle-ci :

Waiting for a string...
Got string: John Doe
Waiting for a dictionary...
Got dictionary with 2 items: {'age': 30, 'city': 'New York'}
Processing complete for this round.
Waiting for a string...
Got string: Jane Smith
Waiting for a dictionary...
Error: Expected type <class 'dict'>

L'instruction yield from rend le code plus propre et plus lisible. Elle nous permet de nous concentrer sur la logique de haut niveau de notre programme plutôt que de nous perdre dans les détails du passage de messages entre les coroutines.

✨ Vérifier la solution et pratiquer

Encapsulation des sockets avec des générateurs

Dans cette étape, nous allons apprendre à utiliser des générateurs pour encapsuler les opérations de socket. C'est un concept très important, surtout en ce qui concerne la programmation asynchrone. La programmation asynchrone permet à votre programme de gérer plusieurs tâches simultanément sans attendre qu'une tâche se termine avant de commencer une autre. L'utilisation de générateurs pour encapsuler les opérations de socket peut rendre votre code plus efficace et plus facile à gérer.

Comprendre le problème

Le fichier server.py contient une implémentation simple d'un serveur réseau utilisant des générateurs. Jetons un coup d'œil au code actuel. Ce code est la base de notre serveur, et il est crucial de le comprendre avant d'apporter des modifications.

def tcp_server(address, handler):
    sock = socket(AF_INET, SOCK_STREAM)
    sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    sock.bind(address)
    sock.listen(5)
    while True:
        yield 'recv', sock
        client, addr = sock.accept()
        tasks.append(handler(client, addr))

def echo_handler(client, address):
    print('Connection from', address)
    while True:
        yield 'recv', client
        data = client.recv(1000)
        if not data:
            break
        yield 'send', client
        client.send(b'GOT:' + data)
    print('Connection closed')
    client.close()

Dans ce code, nous utilisons le mot-clé yield. Le mot-clé yield est utilisé en Python pour créer des générateurs. Un générateur est un type spécial d'itérateur qui vous permet de mettre en pause et de reprendre l'exécution d'une fonction. Ici, yield est utilisé pour indiquer quand le serveur est prêt à recevoir une connexion ou quand un gestionnaire de client est prêt à recevoir ou à envoyer des données. Cependant, les instructions yield manuelles exposent le fonctionnement interne de la boucle d'événements à l'utilisateur. Cela signifie que l'utilisateur doit savoir comment fonctionne la boucle d'événements, ce qui peut rendre le code plus difficile à comprendre et à maintenir.

Création d'une classe GenSocket

Créons une classe GenSocket pour encapsuler les opérations de socket avec des générateurs. Cela rendra notre code plus propre et plus lisible. En encapsulant les opérations de socket dans une classe, nous pouvons cacher les détails de la boucle d'événements à l'utilisateur et nous concentrer sur la logique de haut niveau du serveur.

  1. Ouvrez le fichier server.py dans l'éditeur :
cd /home/labex/project

Cette commande change le répertoire courant pour le répertoire du projet où se trouve le fichier server.py. Une fois que vous êtes dans le bon répertoire, vous pouvez ouvrir le fichier dans votre éditeur de texte préféré.

  1. Ajoutez la classe GenSocket suivante à la fin du fichier, avant toute fonction existante :
class GenSocket:
    """
    A generator-based wrapper for socket operations.
    """
    def __init__(self, sock):
        self.sock = sock

    def accept(self):
        """Accept a connection and return a new GenSocket"""
        yield 'recv', self.sock
        client, addr = self.sock.accept()
        return GenSocket(client), addr

    def recv(self, maxsize):
        """Receive data from the socket"""
        yield 'recv', self.sock
        return self.sock.recv(maxsize)

    def send(self, data):
        """Send data to the socket"""
        yield 'send', self.sock
        return self.sock.send(data)

    def __getattr__(self, name):
        """Forward any other attributes to the underlying socket"""
        return getattr(self.sock, name)

Cette classe GenSocket agit comme un wrapper pour les opérations de socket. La méthode __init__ initialise la classe avec un objet socket. Les méthodes accept, recv et send effectuent les opérations de socket correspondantes et utilisent yield pour indiquer quand l'opération est prête. La méthode __getattr__ permet à la classe de transférer tous les autres attributs à l'objet socket sous-jacent.

  1. Maintenant, modifiez les fonctions tcp_server et echo_handler pour utiliser la classe GenSocket :
def tcp_server(address, handler):
    sock = GenSocket(socket(AF_INET, SOCK_STREAM))
    sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    sock.bind(address)
    sock.listen(5)
    while True:
        client, addr = yield from sock.accept()
        tasks.append(handler(client, addr))

def echo_handler(client, address):
    print('Connection from', address)
    while True:
        data = yield from client.recv(1000)
        if not data:
            break
        yield from client.send(b'GOT:' + data)
    print('Connection closed')
    client.close()

Remarquez comment les instructions explicites yield 'recv', sock et yield 'send', client ont été remplacées par des expressions yield from plus propres. Le mot-clé yield from est utilisé pour déléguer l'exécution à un autre générateur. Cela rend le code plus lisible et cache les détails de la boucle d'événements à l'utilisateur. Maintenant, le code ressemble plus à des appels de fonction normaux, et l'utilisateur n'a pas à se soucier du fonctionnement interne de la boucle d'événements.

  1. Ajoutons une fonction de test simple pour démontrer comment notre serveur serait utilisé :
def run_server():
    """Start the server on port 25000"""
    tasks.append(tcp_server(('localhost', 25000), echo_handler))
    try:
        event_loop()
    except KeyboardInterrupt:
        print("Server stopped")

if __name__ == '__main__':
    print("Starting echo server on port 25000...")
    print("Press Ctrl+C to stop")
    run_server()

Ce code est plus lisible et plus maintenable. La classe GenSocket encapsule la logique de rendu, permettant au code du serveur de se concentrer sur le flux de haut niveau plutôt que sur les détails de la boucle d'événements. La fonction run_server démarre le serveur sur le port 25000 et gère l'exception KeyboardInterrupt, ce qui permet à l'utilisateur d'arrêter le serveur en appuyant sur Ctrl+C.

Comprendre les avantages

L'approche yield from offre plusieurs avantages :

  1. Code plus propre : Les opérations de socket ressemblent plus à des appels de fonction normaux. Cela rend le code plus facile à lire et à comprendre, surtout pour les débutants.
  2. Abstraction : Les détails de la boucle d'événements sont cachés à l'utilisateur. L'utilisateur n'a pas besoin de savoir comment fonctionne la boucle d'événements pour utiliser le code du serveur.
  3. Lisibilité : Le code exprime mieux ce qu'il fait plutôt que comment il le fait. Cela rend le code plus explicite et plus facile à maintenir.
  4. Maintenabilité : Les modifications apportées à la boucle d'événements n'auront pas besoin de modifier le code du serveur. Cela signifie que si vous avez besoin de modifier la boucle d'événements à l'avenir, vous pouvez le faire sans affecter le code du serveur.

Ce modèle est un tremplin vers la syntaxe async/await moderne, que nous explorerons dans l'étape suivante. La syntaxe async/await est un moyen plus avancé et plus propre d'écrire du code asynchrone en Python, et la compréhension du modèle yield from vous aidera à passer à elle plus facilement.

✨ Vérifier la solution et pratiquer

De générateurs à async/await

Dans cette étape finale, nous allons explorer comment le modèle yield from en Python a évolué vers la syntaxe moderne async/await. Comprendre cette évolution est crucial car elle vous aide à voir le lien entre les générateurs et la programmation asynchrone. La programmation asynchrone permet à votre programme de gérer plusieurs tâches sans attendre que chacune d'elles se termine, ce qui est particulièrement utile en programmation réseau et dans d'autres opérations liées à l'E/S (I/O).

Le lien entre les générateurs et async/await

La syntaxe async/await, introduite en Python 3.5, est construite sur la base des fonctionnalités des générateurs et de yield from. En réalité, les fonctions async sont implémentées à l'aide de générateurs. Cela signifie que les concepts que vous avez appris sur les générateurs sont directement liés au fonctionnement de async/await.

Pour passer de l'utilisation des générateurs à la syntaxe async/await, nous devons suivre ces étapes :

  1. Utiliser le décorateur @coroutine du module types. Ce décorateur aide à convertir les fonctions basées sur les générateurs en une forme utilisable avec async/await.
  2. Convertir les fonctions utilisant yield from pour utiliser async et await à la place. Cela rend le code plus lisible et exprime mieux la nature asynchrone des opérations.
  3. Mettre à jour la boucle d'événements pour gérer les coroutines natives. La boucle d'événements est responsable de la planification et de l'exécution des tâches asynchrones.

Mise à jour de la classe GenSocket

Maintenant, modifions notre classe GenSocket pour qu'elle fonctionne avec le décorateur @coroutine. Cela permettra à notre classe d'être utilisée dans un contexte async/await.

  1. Ouvrez le fichier server.py dans l'éditeur. Vous pouvez le faire en exécutant la commande suivante dans le terminal :
cd /home/labex/project
  1. En haut du fichier server.py, ajoutez l'import pour coroutine. Cet import est nécessaire pour utiliser le décorateur @coroutine.
from types import coroutine
  1. Mettez à jour la classe GenSocket pour utiliser le décorateur @coroutine. Ce décorateur transforme nos méthodes basées sur les générateurs en coroutines attendables, ce qui signifie qu'elles peuvent être utilisées avec le mot - clé await.
class GenSocket:
    """
    A generator-based wrapper for socket operations
    that works with async/await.
    """
    def __init__(self, sock):
        self.sock = sock

    @coroutine
    def accept(self):
        """Accept a connection and return a new GenSocket"""
        yield 'recv', self.sock
        client, addr = self.sock.accept()
        return GenSocket(client), addr

    @coroutine
    def recv(self, maxsize):
        """Receive data from the socket"""
        yield 'recv', self.sock
        return self.sock.recv(maxsize)

    @coroutine
    def send(self, data):
        """Send data to the socket"""
        yield 'send', self.sock
        return self.sock.send(data)

    def __getattr__(self, name):
        """Forward any other attributes to the underlying socket"""
        return getattr(self.sock, name)

Conversion vers la syntaxe async/await

Ensuite, convertissons notre code de serveur pour utiliser la syntaxe async/await. Cela rendra le code plus lisible et exprimera clairement la nature asynchrone des opérations.

async def tcp_server(address, handler):
    """
    An asynchronous TCP server using async/await.
    """
    sock = GenSocket(socket(AF_INET, SOCK_STREAM))
    sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    sock.bind(address)
    sock.listen(5)
    while True:
        client, addr = await sock.accept()
        tasks.append(handler(client, addr))

async def echo_handler(client, address):
    """
    An asynchronous handler for echo clients.
    """
    print('Connection from', address)
    while True:
        data = await client.recv(1000)
        if not data:
            break
        await client.send(b'GOT:' + data)
    print('Connection closed')
    client.close()

Remarquez que yield from a été remplacé par await, et les fonctions sont maintenant définies avec async def au lieu de def. Ce changement rend le code plus intuitif et plus facile à comprendre.

Comprendre la transformation

Le passage des générateurs avec yield from à la syntaxe async/await n'est pas seulement un simple changement syntaxique. Il représente un changement dans la façon dont nous pensons la programmation asynchrone.

  1. Générateurs avec yield from :

    • Lorsque vous utilisez des générateurs avec yield from, vous cédez explicitement le contrôle pour signaler qu'une tâche est prête. Cela signifie que vous devez gérer manuellement le moment où une tâche peut continuer.
    • Vous devez également gérer manuellement la planification des tâches. Cela peut être complexe, surtout dans les programmes plus grands.
    • L'accent est mis sur la mécanique du flux de contrôle, ce qui peut rendre le code plus difficile à lire et à maintenir.
  2. Syntaxe async/await :

    • Avec la syntaxe async/await, le contrôle est cédé implicitement aux points await. Cela rend le code plus simple car vous n'avez pas à vous soucier de céder explicitement le contrôle.
    • La boucle d'événements s'occupe de la planification des tâches, vous n'avez donc pas à la gérer manuellement.
    • L'accent est mis sur le flux logique du programme, ce qui rend le code plus lisible et plus maintenable.

Cette transformation permet d'obtenir un code asynchrone plus lisible et plus maintenable, ce qui est particulièrement important pour les applications complexes comme les serveurs réseau.

Programmation asynchrone moderne

En Python moderne, nous utilisons généralement le module asyncio pour la programmation asynchrone au lieu d'une boucle d'événements personnalisée. Le module asyncio offre une prise en charge intégrée de nombreuses fonctionnalités utiles :

  • Exécution de plusieurs coroutines de manière concurrente. Cela permet à votre programme de gérer plusieurs tâches en même temps.
  • Gestion de l'E/S réseau. Il simplifie le processus d'envoi et de réception de données sur le réseau.
  • Primitives de synchronisation. Elles vous aident à gérer l'accès aux ressources partagées dans un environnement concurrent.
  • Planification et annulation de tâches. Vous pouvez facilement planifier des tâches pour qu'elles s'exécutent à des moments spécifiques et les annuler si nécessaire.

Voici à quoi pourrait ressembler notre serveur en utilisant asyncio :

import asyncio

async def handle_client(reader, writer):
    addr = writer.get_extra_info('peername')
    print(f'Connection from {addr}')

    while True:
        data = await reader.read(1000)
        if not data:
            break

        writer.write(b'GOT:' + data)
        await writer.drain()

    print('Connection closed')
    writer.close()
    await writer.wait_closed()

async def main():
    server = await asyncio.start_server(
        handle_client, 'localhost', 25000
    )

    addr = server.sockets[0].getsockname()
    print(f'Serving on {addr}')

    async with server:
        await server.serve_forever()

if __name__ == '__main__':
    asyncio.run(main())

Ce code atteint la même fonctionnalité que notre serveur basé sur les générateurs, mais utilise la bibliothèque standard asyncio, qui est plus robuste et riche en fonctionnalités.

Conclusion

Dans ce laboratoire, vous avez appris plusieurs concepts importants :

  1. L'instruction yield from et comment elle délègue à un autre générateur. C'est un concept fondamental pour comprendre le fonctionnement des générateurs.
  2. Comment utiliser yield from avec les coroutines pour le passage de messages. Cela vous permet de communiquer entre différentes parties de votre programme asynchrone.
  3. Encapsuler les opérations de socket avec des générateurs pour un code plus propre. Cela rend votre code lié au réseau plus organisé et plus facile à comprendre.
  4. La transition des générateurs vers la syntaxe moderne async/await. Comprendre cette transition vous aidera à écrire un code asynchrone plus lisible et plus maintenable en Python, que vous utilisiez directement les générateurs ou la syntaxe moderne async/await.
✨ Vérifier la solution et pratiquer

Résumé

Dans ce laboratoire, vous avez appris le concept de délégation de générateurs en Python, en mettant l'accent sur l'instruction yield from et ses diverses applications. Vous avez exploré comment utiliser yield from pour déléguer à un autre générateur, ce qui simplifie le code et améliore sa lisibilité. Vous avez également appris à créer des coroutines avec yield from pour recevoir et valider des messages, ainsi qu'à utiliser des générateurs pour encapsuler les opérations de socket afin d'obtenir un code réseau plus propre.

Ces concepts sont essentiels pour comprendre la programmation asynchrone en Python. La transition des générateurs vers la syntaxe moderne async/await représente un progrès significatif dans la gestion des opérations asynchrones. Pour approfondir ces concepts, vous pouvez étudier le module asyncio, examiner comment les frameworks populaires utilisent async/await et développer vos propres bibliothèques asynchrones. Comprendre la délégation de générateurs et yield from offre une vision plus approfondie de l'approche de Python en matière de programmation asynchrone.