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.
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 :
gen = (n * n for n in range(5))
next(gen) # 0 (calculé maintenant)
next(gen) # 1
next(gen) # 4
# ... et ainsi de suite, jusqu'à StopIterationIl 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 :
total = sum(n * n for n in range(1_000_000))
# aucune liste intermédiaire, mémoire constantesum, 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 :
sum((n * n for n in range(10))) # OK
sum(n * n for n in range(10)) # IDENTIQUE, plus lisibleany 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.
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)
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:
breakAucune 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 :
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
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`.
Afficher l'indice
range(1, 101) inclut 100.
Solution disponible après 3 tentatives
Exercice de révision
É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`.
Afficher l'indice
any(p.isupper() for p in words)
Solution disponible après 3 tentatives
Défi supplémentaire
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.
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