Exercice 01 : Coroutine pour Filtrer des Données
Objectif
Cet exercice a pour but de vous faire créer une coroutine (basée sur un générateur) qui reçoit des données, les filtre selon un critère, et les envoie à une autre coroutine "cible". Cela illustre comment chaîner des coroutines pour créer un pipeline de traitement.
Contexte
Les pipelines de coroutines sont un patron de conception puissant pour traiter des flux de données. Chaque coroutine agit comme une étape de traitement : elle reçoit des données de la source (ou de l'étape précédente), effectue une opération, et passe le résultat à l'étape suivante.
Dans cet exercice, vous allez construire un pipeline simple à deux étapes :
- Une coroutine qui filtre des nombres pour ne garder que les multiples d'un certain nombre.
- Une coroutine "puits" (
sink) qui reçoit les données filtrées et les affiche.
Énoncé
-
Créez un nouveau fichier Python nommé
coroutine_pipeline.py. -
Créez une coroutine "puits"
print_sink.- Cette coroutine doit être décorée avec un décorateur d'amorçage (voir ci-dessous) pour éviter d'avoir à appeler
next()manuellement. - Elle doit tourner dans une boucle infinie (
while True). - À chaque itération, elle attend de recevoir une valeur avec
(yield). - Elle affiche la valeur reçue avec un préfixe, par exemple :
f"Sink: a reçu {valeur}".
- Cette coroutine doit être décorée avec un décorateur d'amorçage (voir ci-dessous) pour éviter d'avoir à appeler
-
Créez un décorateur d'amorçage
@coroutine.- Ce décorateur prend une fonction générateur en argument.
- Il définit une fonction wrapper qui :
- Crée le générateur en appelant la fonction.
- Amorce le générateur en appelant
next()dessus. - Retourne le générateur amorcé.
- Ce décorateur simplifiera grandement l'utilisation de nos coroutines.
-
Créez la coroutine de filtrage
filter_multiple.- Décorez-la avec
@coroutine. - Elle doit accepter deux arguments :
multiple(l'entier pour le test de divisibilité) ettarget(la coroutine cible où envoyer les résultats). - Dans une boucle infinie, elle doit :
- Attendre de recevoir un nombre avec
nombre = (yield). - Vérifier si
nombreest un multiple demultiple. - Si c'est le cas, envoyer ce nombre à la coroutine
targeten utilisanttarget.send(nombre).
- Attendre de recevoir un nombre avec
- Décorez-la avec
-
Mettez en place le pipeline dans le bloc principal.
- Créez une instance de la coroutine puits
print_sink. - Créez une instance de la coroutine de filtrage
filter_multiple, en lui passant3comme multiple et leprint_sinkcomme cible. - Envoyez une série de nombres (de 1 à 10, par exemple) à la coroutine de filtrage en utilisant une boucle
foret la méthodesend().
- Créez une instance de la coroutine puits
Résultat Attendu
Seuls les nombres multiples de 3 doivent être affichés par la coroutine "puits".
Sink: a reçu 3
Sink: a reçu 6
Sink: a reçu 9
Cliquez ici pour voir un exemple de code de solution
# coroutine_pipeline.py
from functools import wraps
def coroutine(func):
"""Décorateur pour amorcer automatiquement une coroutine."""
@wraps(func)
def wrapper(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen) # Amorce le générateur
return gen
return wrapper
@coroutine
def print_sink():
"""Coroutine qui agit comme un puits de données, affichant ce qu'elle reçoit."""
print("Sink: Prêt à recevoir des données.")
try:
while True:
valeur = (yield)
print(f"Sink: a reçu {valeur}")
except GeneratorExit:
print("Sink: Fermeture.")
@coroutine
def filter_multiple(multiple: int, target):
"""
Coroutine qui reçoit des nombres, filtre les multiples de 'multiple',
et les envoie à 'target'.
"""
print(f"Filter: Prêt à filtrer les multiples de {multiple}.")
try:
while True:
nombre = (yield)
if nombre % multiple == 0:
target.send(nombre)
except GeneratorExit:
target.close() # Propage la fermeture à la cible
# --- Mise en place du pipeline ---
if __name__ == "__main__":
# 1. Créer le puits (la fin du pipeline)
sink = print_sink()
# 2. Créer le filtre, en le connectant au puits
# Le filtre enverra ses résultats à 'sink'
filtrage = filter_multiple(3, sink)
# 3. Envoyer des données au début du pipeline (le filtre)
print("\nEnvoi des données au pipeline...")
for i in range(1, 11):
print(f"Source: envoi de {i}")
filtrage.send(i)
# 4. Fermer le pipeline
print("\nFermeture du pipeline.")
filtrage.close()