Table of Contents
Introduction
Les comprehensions de listes et les expressions generatrices sont parmi les fonctionnalites les plus elegantes et puissantes de Python. Elles permettent d’ecrire du code plus concis, plus lisible et souvent plus performant que les boucles traditionnelles.
Pourquoi maitriser ces concepts ?
En tant que developpeur Python, vous rencontrerez ces constructions dans pratiquement tous les projets professionnels. Elles sont essentielles pour :
- Ecrire du code pythonique : Les comprehensions sont considerees comme la maniere idiomatique de creer des collections en Python
- Ameliorer les performances : Elles sont optimisees au niveau de l’interpreteur Python
- Reduire la complexite : Un code plus court signifie moins de bugs potentiels
- Economiser la memoire : Les generateurs permettent de traiter de grandes quantites de donnees sans les charger entierement en memoire
Dans ce guide complet, nous explorerons ces concepts en profondeur avec des exemples pratiques que vous pourrez utiliser dans vos projets.
Les Comprehensions de Listes
Les comprehensions de listes sont une syntaxe concise pour creer des listes a partir d’iterables existants. Elles remplacent avantageusement les boucles for classiques dans de nombreux cas.
Syntaxe de base
# Syntaxe generale
[expression for element in iterable if condition]
# Equivalent avec une boucle for traditionnelle
result = []
for element in iterable:
if condition:
result.append(expression)
Exemples pratiques
# Creer une liste des carres des nombres de 1 a 10
carres = [x**2 for x in range(1, 11)]
# Resultat: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# Filtrer les nombres pairs
pairs = [x for x in range(1, 21) if x % 2 == 0]
# Resultat: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# Transformer une liste de chaines en majuscules
noms = ['alice', 'bob', 'charlie']
noms_majuscules = [nom.upper() for nom in noms]
# Resultat: ['ALICE', 'BOB', 'CHARLIE']
# Creer une liste des nombres impairs de 1 a 10
impairs = [x for x in range(1, 11) if x % 2 != 0]
# Resultat: [1, 3, 5, 7, 9]
Comprehensions imbriquees
Les comprehensions peuvent etre imbriquees pour traiter des structures de donnees multidimensionnelles.
# Aplatir une liste de listes
matrice = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
aplatie = [element for ligne in matrice for element in ligne]
# Resultat: [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Creer une matrice 3x3 de zeros
matrice_zeros = [[0 for _ in range(3)] for _ in range(3)]
# Resultat: [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
# Transposer une matrice
matrice = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposee = [[ligne[i] for ligne in matrice] for i in range(len(matrice[0]))]
# Resultat: [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Comprehensions avec conditions multiples
# Filtrer avec plusieurs conditions
nombres = [x for x in range(1, 50) if x % 2 == 0 if x % 3 == 0]
# Nombres divisibles par 2 ET par 3
# Resultat: [6, 12, 18, 24, 30, 36, 42, 48]
# Utiliser l'expression conditionnelle (ternaire)
resultats = ['pair' if x % 2 == 0 else 'impair' for x in range(1, 6)]
# Resultat: ['impair', 'pair', 'impair', 'pair', 'impair']
Les Expressions Generatrices
Les expressions generatrices sont similaires aux comprehensions de listes mais creent un objet generateur au lieu d’une liste complete en memoire. Elles sont ideales pour traiter de grandes quantites de donnees.
Syntaxe et difference avec les listes
# Expression generatrice (parentheses au lieu de crochets)
gen = (x**2 for x in range(1, 11))
# La difference cruciale : le generateur ne calcule pas tout immediatement
print(type(gen)) # <class 'generator'>
# Les valeurs sont calculees a la demande (lazy evaluation)
print(next(gen)) # 1
print(next(gen)) # 4
print(next(gen)) # 9
Avantages en termes de memoire
import sys
# Comparaison de l'utilisation memoire
liste = [x**2 for x in range(1000000)]
generateur = (x**2 for x in range(1000000))
print(f"Taille liste: {sys.getsizeof(liste)} octets")
# Environ 8 MB
print(f"Taille generateur: {sys.getsizeof(generateur)} octets")
# Environ 120 octets seulement !
Exemples pratiques avec les generateurs
# Traiter un fichier ligne par ligne sans tout charger en memoire
def lignes_non_vides(fichier):
return (ligne.strip() for ligne in fichier if ligne.strip())
# Calculer la somme de grands ensembles de donnees
somme = sum(x**2 for x in range(1000000))
# Filtrer et transformer en une seule passe
donnees = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
resultat = sum(x * 2 for x in donnees if x % 2 == 0)
# Resultat: 60 (somme des doubles des nombres pairs)
# Chainer plusieurs transformations
noms = [' Alice ', 'BOB', ' charlie ']
normalises = (nom.strip().lower() for nom in noms)
# Genere: 'alice', 'bob', 'charlie' a la demande
Creer des generateurs personnalises avec yield
# Generateur avec la fonction yield
def fibonacci(n):
"""Genere les n premiers nombres de Fibonacci."""
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
# Utilisation
for num in fibonacci(10):
print(num, end=' ')
# Resultat: 0 1 1 2 3 5 8 13 21 34
# Generateur infini (utiliser avec precaution)
def nombres_pairs():
"""Genere tous les nombres pairs indefiniment."""
n = 0
while True:
yield n
n += 2
Comprehensions de Dictionnaires et d’Ensembles
Python offre egalement des comprehensions pour les dictionnaires et les ensembles.
# Comprehension de dictionnaire
carres_dict = {x: x**2 for x in range(1, 6)}
# Resultat: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Inverser un dictionnaire
original = {'a': 1, 'b': 2, 'c': 3}
inverse = {v: k for k, v in original.items()}
# Resultat: {1: 'a', 2: 'b', 3: 'c'}
# Comprehension d'ensemble (elements uniques)
lettres = {lettre.lower() for lettre in 'ABRACADABRA'}
# Resultat: {'a', 'b', 'c', 'd', 'r'}
# Filtrer un dictionnaire
scores = {'alice': 85, 'bob': 72, 'charlie': 90, 'diana': 68}
reussis = {nom: score for nom, score in scores.items() if score >= 75}
# Resultat: {'alice': 85, 'charlie': 90}
Bonnes Pratiques
1. Privilegier la lisibilite
# Mauvais : comprehension trop complexe
result = [x.strip().lower() for x in data if x and len(x.strip()) > 3 and not x.startswith('#')]
# Bon : utiliser une fonction pour la clarte
def est_valide(x):
x = x.strip()
return x and len(x) > 3 and not x.startswith('#')
result = [x.strip().lower() for x in data if est_valide(x)]
2. Utiliser les generateurs pour les grandes donnees
# Mauvais : cree une liste intermediaire enorme
somme = sum([x**2 for x in range(10000000)]) # Consomme beaucoup de memoire
# Bon : utilise un generateur
somme = sum(x**2 for x in range(10000000)) # Memoire constante
3. Eviter les effets de bord
# Mauvais : modifier une variable externe dans une comprehension
compteur = 0
result = [compteur := compteur + 1 for _ in range(5)] # A eviter
# Bon : utiliser enumerate ou range pour les compteurs
result = list(range(1, 6))
4. Limiter l’imbrication a deux niveaux
# Mauvais : trois niveaux d'imbrication
result = [x for groupe in data for sous_groupe in groupe for x in sous_groupe]
# Bon : utiliser une fonction auxiliaire
def aplatir(data):
for groupe in data:
for sous_groupe in groupe:
yield from sous_groupe
result = list(aplatir(data))
5. Preferer les fonctions natives quand disponibles
# Au lieu de : [str(x) for x in nombres]
# Utiliser : list(map(str, nombres))
# Au lieu de : [x for x in nombres if condition(x)]
# Utiliser : list(filter(condition, nombres))
Pieges Courants
1. Oublier que les generateurs sont a usage unique
# Piege : reutiliser un generateur epuise
gen = (x**2 for x in range(5))
liste1 = list(gen) # [0, 1, 4, 9, 16]
liste2 = list(gen) # [] - Le generateur est vide !
# Solution : creer un nouveau generateur ou utiliser une liste
2. Modifier une liste pendant l’iteration
# Piege : peut causer des comportements inattendus
nombres = [1, 2, 3, 4, 5]
# NE PAS FAIRE : [nombres.remove(x) for x in nombres if x % 2 == 0]
# Solution : creer une nouvelle liste
nombres = [x for x in nombres if x % 2 != 0]
3. Probleme de portee avec les variables de boucle
# Piege : toutes les fonctions lambda partagent la meme variable
fonctions = [lambda: x for x in range(5)]
print([f() for f in fonctions]) # [4, 4, 4, 4, 4] - Pas ce qu'on voulait !
# Solution : capturer la valeur avec un argument par defaut
fonctions = [lambda x=x: x for x in range(5)]
print([f() for f in fonctions]) # [0, 1, 2, 3, 4]
4. Performances degradees avec des conditions complexes
# Piege : evaluer une fonction couteuse plusieurs fois
result = [process(x) for x in data if validate(process(x))] # process() appele 2 fois
# Solution : utiliser l'operateur walrus (Python 3.8+)
result = [y for x in data if validate(y := process(x))]
Comparaison des Performances
import timeit
# Benchmark : list comprehension vs boucle for
def avec_boucle():
result = []
for i in range(1000):
result.append(i ** 2)
return result
def avec_comprehension():
return [i ** 2 for i in range(1000)]
print(f"Boucle: {timeit.timeit(avec_boucle, number=10000):.4f}s")
print(f"Comprehension: {timeit.timeit(avec_comprehension, number=10000):.4f}s")
# La comprehension est generalement 20-30% plus rapide
Conclusion
Les list comprehensions et les expressions generatrices sont des outils essentiels dans l’arsenal de tout developpeur Python. Elles permettent d’ecrire du code plus elegant, plus lisible et souvent plus performant.
Points cles a retenir :
- Utilisez les list comprehensions pour creer des listes de taille raisonnable
- Privilegiez les generateurs pour traiter de grandes quantites de donnees
- Gardez vos comprehensions simples et lisibles - si elles deviennent trop complexes, utilisez une fonction
- N’oubliez pas les dict et set comprehensions pour les autres types de collections
- Faites attention aux pieges courants comme la reutilisation des generateurs
En maitrisant ces concepts, vous ecrirez du code Python plus idiomatique et professionnel. N’hesitez pas a experimenter avec ces constructions dans vos propres projets pour en tirer le meilleur parti.
Pour aller plus loin
- Explorez le module
itertoolspour des operations avancees sur les iterables - Decouvrez les fonctions
map(),filter()etreduce()pour des approches fonctionnelles - Apprenez a creer vos propres iterateurs avec les methodes
__iter__et__next__
In-Article Ad
Dev Mode
Tags
Mahmoud DEVO
Senior Full-Stack Developer
I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.
Enjoyed this article?
Subscribe to get more tech content delivered to your inbox.
Related Articles
Commenter la lecture de listes en Python : exemples et astuc
Voici une proposition de meta description qui répond aux critères : "Apprenez comment itérer efficacement sur vos listes en Python avec des exemples concrets !
Recherche de valeurs dans les listes, tuples et dictionnaires Python
Apprenez a rechercher des elements dans les sequences Python : methode index(), mot-cle in, recherche dans les dictionnaires et algorithme bisect pour listes triees.
Expressions Regulieres en Python : Guide Complet pour Maitriser le Module re
Apprenez a maitriser les expressions regulieres en Python avec le module re. Decouvrez comment extraire des donnees, valider des formats, manipuler des chaines et eviter les pieges courants avec des exemples pratiques.