Passer au contenu principal
eLearner.app
Module 6 · Leçon 3 sur 423/36 dans le cours~10 min
Leçons du module (3/4)

Generator expressions

Une expression génératrice (generator expression) est semblable à une compréhension de liste, mais avec des parenthèses au lieu de crochets. La différence est spectaculaire : elle ne construit pas de liste en mémoire, elle produit les éléments à la demande, un à la fois.

Python
gen = (n * n for n in range(1_000_000))
# instantané, mémoire constante : aucun calcul n'est encore effectué

list_comp = [n * n for n in range(1_000_000)]
# alloue 1M d'entiers en mémoire

Évaluation paresseuse (lazy evaluation)

Le générateur ne fait rien tant que personne ne demande la valeur suivante :

Python
gen = (n * n for n in range(5))
next(gen)    # 0    (calculé maintenant)
next(gen)    # 1
next(gen)    # 4
# ... et ainsi de suite, jusqu'à StopIteration

Il s'épuise : après avoir consommé tous les éléments, il reste vide pour toujours. Pour le réutiliser, vous devez le recréer.

Quand l'utiliser

Lorsque vous passez les résultats à une fonction qui les consomme à la suite :

Python
total = sum(n * n for n in range(1_000_000))
# aucune liste intermédiaire, mémoire constante

sum, any, all, max, min, "".join(...) acceptent tous des générateurs. Si la fonction externe ne nécessite que les parenthèses d'appel, vous pouvez omettre celles du générateur :

Python
sum((n * n for n in range(10)))    # OK
sum(n * n for n in range(10))      # IDENTIQUE, plus lisible

any et all : court-circuit

any(...) et all(...) s'arrêtent dès qu'ils ont la réponse. Avec un générateur, les éléments suivants ne sont même pas calculés.

Python
nums = [1, 2, 3, 4, 5]
any(n > 3 for n in nums)    # True (s'arrête à 4)
all(n > 0 for n in nums)    # True
all(n > 2 for n in nums)    # False (s'arrête à 1)

Modèle : pipeline paresseux (lazy pipeline)

Python
righe = (linea.strip() for linea in testo.splitlines())
non_vuote = (r for r in righe if r)
prime_dieci = []
for r in non_vuote:
    prime_dieci.append(r)
    if len(prime_dieci) == 10:
        break

Aucune liste intermédiaire, même sur de très grandes entrées.

Consommer des générateurs avec sum, any, all

Les expressions génératrices sont parfaites pour être passées directement à des fonctions qui consomment des itérables, comme sum(), any() ou all(). Dans ce cas, vous pouvez omettre les parenthèses externes de l'expression génératrice, en écrivant simplement :

Python
squares_sum = sum(x**2 for x in range(10))

Cela permet de garder votre code propre et très économe en mémoire.

À vous de jouer

Exercice#python.m6.l3.e1
Tentatives : 0Chargement…

Calculez la somme des carrés des nombres de 1 à 100 (inclus) en utilisant une expression génératrice à l'intérieur de sum(). Assignez-la à `tot`. Évaluez `tot`.

Chargement de l'éditeur…
Afficher l'indice

range(1, 101) inclut 100.

Solution disponible après 3 tentatives

Exercice de révision

Exercice#python.m6.l3.e2
Tentatives : 0Chargement…

Étant donné la liste `words = ['ciao', 'mondo', 'PYTHON', 'java']`, utilisez any() avec une expression génératrice pour vérifier si AU MOINS un mot est en majuscules (.isupper()). Assignez le résultat à `has_uppercase`. Évaluez `has_uppercase`.

Chargement de l'éditeur…
Afficher l'indice

any(p.isupper() for p in words)

Solution disponible après 3 tentatives

Défi supplémentaire

Exercice#python.m6.l3.e3
Tentatives : 0Chargement…

Utilisez une expression génératrice passée directement à la fonction `sum()` pour calculer la somme des carrés de tous les entiers de 1 à 100 inclusivement. Stockez le résultat dans `total_sum` et évaluez-le.

Chargement de l'éditeur…
Afficher l'indice

Écrivez total_sum = sum(x**2 for x in range(1, 101)). Les doubles parenthèses ne sont pas nécessaires.

Solution disponible après 3 tentatives