List Comprehensions et Generateurs en Python : Guide Complet et Optimisation des Performances

Maitrisez les list comprehensions et expressions generatrices en Python. Apprenez a creer des listes et iterables de maniere concise, performante et pythonique avec des exemples pratiques.

Mahmoud DEVO
Mahmoud DEVO
December 27, 2025 7 min read
List Comprehensions et Generateurs en Python : Guide Complet et Optimisation des Performances

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 itertools pour des operations avancees sur les iterables
  • Decouvrez les fonctions map(), filter() et reduce() pour des approches fonctionnelles
  • Apprenez a creer vos propres iterateurs avec les methodes __iter__ et __next__
Advertisement

In-Article Ad

Dev Mode

Share this article

Mahmoud DEVO

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