Maîtriser les générateurs Python pour les certifications avancées
Découvrez comment exploiter les générateurs pour écrire du code Python à la fois lisible, performant et conforme aux exigences des certifications comme PCAP ou AWS.
Introduction
Les générateurs sont souvent présentés comme une alternative aux listes lorsqu’on veut économiser de la mémoire. Dans le cadre d’une certification, il faut toutefois aller plus loin : comprendre leur mécanisme d’exécution, savoir les combiner avec d’autres constructions du langage et les optimiser pour des scénarios réels. Cet article vous propose une vue d’ensemble, des exemples concrets et des bonnes pratiques afin d’utiliser les générateurs de façon professionnelle.
Principes fondamentaux des générateurs
Un générateur est une fonction Python qui utilise le mot‑clé yield au lieu de return. À chaque appel, il produit une valeur puis suspend son état d’exécution, conservant les variables locales, la position du curseur et le contexte d’appel. Cette capacité de « suspend‑resume » repose sur les coroutines légères implémentées dans le moteur CPython.
def compteur(maximum: int):
i = 0
while i < maximum:
yield i # suspend et renvoie la valeur i
i += 1
Lorsque le générateur est itéré, le moteur exécute le code jusqu’à yield, renvoie la valeur, puis reprend là‑dessus à la prochaine itération. Cette sémantique garantit que la mémoire utilisée ne dépend que du nombre d’objets actifs, pas du nombre total d’éléments produits.
Cas d’usage avancés
1. Chaînage de générateurs (pipeline)
On peut composer plusieurs générateurs pour créer un pipeline de traitement, à la manière des filtres Unix. Chaque étape consomme un élément, le transforme et le transmet.
def filtrer_pairs(it):
for n in it:
if n % 2 == 0:
yield n
def carre(it):
for n in it:
yield n * n
# Chaînage
source = compteur(10)
pipeline = carre(filtrer_pairs(source))
print(list(pipeline)) # [0, 4, 16, 36, 64]
Cette approche évite les listes intermédiaires et facilite la lecture du flux de données.
2. Générateurs asynchrones (async def + yield)
Pour les API réseau ou le traitement de fichiers volumineux, la combinaison async/await avec async for permet de garder un seul thread tout en restant non bloquant.
import aiohttp
async def fetch_urls(urls):
async with aiohttp.ClientSession() as session:
for url in urls:
async with session.get(url) as resp:
yield await resp.text()
Le consommateur peut alors faire :
async for page in fetch_urls(['https://example.com', 'https://python.org']):
process(page)
3. Générateurs avec send pour l’injection de données
Le mécanisme send permet d’envoyer une valeur dans le générateur au moment où il est suspendu. Cela est utile pour implémenter des coroutines de type « producer‑consumer ».
def accumulateur():
total = 0
while True:
delta = yield total
total += delta
acc = accumulateur()
next(acc) # démarre le générateur, renvoie 0
print(acc.send(5)) # 5
print(acc.send(3)) # 8
Ce pattern apparaît souvent dans les questions d’examen où l’on doit expliquer le déroulement d’un générateur.
Optimisations et bonnes pratiques
- Limiter les appels
yielddans les boucles critiques : chaqueyieldimplique une sauvegarde de l’état, ce qui a un coût non négligeable. Si la boucle est très courte, il peut être plus rapide de construire une petite liste locale puis deyieldle résultat complet. - Utiliser
itertoolspour les opérations standards : fonctions commeitertools.islice,itertools.chainouitertools.filterfalsesont implémentées en C et offrent des performances supérieures aux implémentations pure Python. - Préférer les compréhensions de générateur (
(x for x in ...)) aux fonctions explicites lorsqu’une logique simple suffit, afin de réduire la surface du code. - Ne jamais modifier la séquence source pendant l’itération : cela peut entraîner des comportements indéfinis. Si une mutation est nécessaire, créez un nouveau générateur à partir d’une copie ou d’une vue immuable.
- Documenter le contrat d’entrée/sortie : indiquez clairement le type d’objet attendu (
Iterable[int],AsyncIterable[str], …) et le type de valeur renvoyée. Les certifications apprécient la rigueur du typage.
Conclusion
Les générateurs ne sont pas qu’un simple moyen d’économiser de la mémoire ; ils constituent un véritable paradigme de programmation fonctionnelle et asynchrone. En maîtrisant le chaînage, l’injection via send et les variantes asynchrones, vous êtes capable de concevoir des pipelines efficaces, lisibles et conformes aux exigences des certifications avancées. Intégrez ces patterns dans vos projets, mesurez leurs performances avec le module timeit, et vous disposerez d’un argument solide lors de vos évaluations.
Envie d’aller plus loin avec CertifApp ?
Découvrir CertifApp