Question 1Quelle est la façon la plus concise de construire le produit cartésien de [1, 2, 3] et [A, B] ?
itertools et functools — Boîtes à outils d'itération et de composition de fonctions
Recettes d'itération combinations / product / chain / accumulate, gel d'argument avec partial, et accélération d'une récursion via la mémoïsation @lru_cache.
itertools est un ensemble de fonctions qui construisent de nouveaux itérables à partir d'itérables existants (combinaisons, concaténation, accumulation), et functools est un ensemble de fonctions qui transforment la fonction elle-même (liaison d'arguments, mémoïsation). Les deux peuvent remplacer les motifs boucle for + suivi d'état en une seule ligne, réduisant drastiquement ton code.
Itérables — valeurs sur lesquelles tu peux boucler
La plupart des fonctions itertools prennent un "itérable" en entrée et retournent un nouvel itérable, donc il vaut la peine de fixer d'abord ce qu'est un itérable. Un itérable (la chose que tu peux écrire à droite de for x in ___:) est tout ce que tu peux passer à une boucle for, à list(...), à sum(...) ou à map(...). list / tuple / str / dict / set / range, plus les générateurs que tu écris toi-même (fonctions qui utilisent yield), se comportent tous comme des itérables en Python.
list / tuple / str / dict / set / range sont des itérables, et les générateurs personnalisés construits avec yield aussi.itertools — boîte à outils de combinaison et d'itération
itertools est un module qui rassemble des fonctions construisant de nouveaux itérables à partir d'itérables existants. Les quatre que tu utiliseras le plus sont combinations (combinaisons non ordonnées), product (produit cartésien sur plusieurs ensembles), chain (concaténer des itérables en un seul) et accumulate (totaux ou produits cumulés). Connaître ces quatre te permet de réécrire les doubles boucles for et les boucles à suivi d'état bien plus proprement.
list(...) pour matérialiser.| Fonction | Entrée → Sortie | Exemple |
|---|---|---|
| combinations(it, k) | Combinaisons à k éléments (non ordonnées) | [A,B,C] → (A,B), (A,C), (B,C) |
| permutations(it, k) | Permutations à k éléments (ordonnées) | [A,B] → (A,B), (B,A) |
| product(*its) | Produit cartésien entre ensembles | [S,M] × [red,blue] → 4 combinaisons |
| chain(*its) | Concaténer plusieurs itérables | [1,2] + [3,4] → [1,2,3,4] |
| accumulate(it) | Accumuler depuis la gauche (somme par défaut) | [10,20,30] → [10, 30, 60] |
functools.partial — lier des arguments à l'avance
functools.partial construit une nouvelle fonction avec certains arguments déjà verrouillés. Quand tu dois passer une fonction avec des arguments liés à un callback ou à une fonction d'ordre supérieur, ça t'évite d'écrire des fonctions wrapper d'une ligne comme def wrapper(...): .... Partout où tu appelles la même fonction de façon répétée avec de légères variations d'arguments, partial coupe le bruit du wrapper et rend le code plus lisible.
from functools import partial
def format_with_unit(price, unit):
return f"{price}{unit}"
# Construit une nouvelle fonction avec l'unité "USD" pré-liée
to_usd = partial(format_with_unit, unit="USD")
# Applique à des prix individuels — seul price doit être passé maintenant
print(to_usd(100)) # 100USD
print(to_usd(200)) # 200USD
print(to_usd(300)) # 300USD
exp=2 sur power(base, exp) et tu obtiens une fonction square qui n'a besoin que de base.functools.lru_cache — mettre en cache les résultats avec mémoïsation
"Je veux réutiliser le résultat d'une fonction coûteuse appelée de façon répétée avec les mêmes arguments" — ça apparaît dès que les sorties d'une fonction sont déterminées par ses entrées et que la vitesse compte. Le faire à la main signifie gérer ton propre dict de cache, mais @lru_cache le fait en une seule ligne de décorateur.
@lru_cache est un décorateur qui met en cache les valeurs retournées d'une fonction. Quand on l'appelle à nouveau avec les mêmes arguments, il retourne directement la valeur en cache, en sautant le recalcul. LRU (Least Recently Used — supprime les entrées les moins récemment utilisées) élimine les vieilles entrées de cache pour que tu puisses limiter l'usage mémoire avec quelque chose comme maxsize=128.
Ne l'ajoute pas aux fonctions à effets de bord
@lru_cache suppose que "mêmes arguments → même valeur retournée". Applique-le à des fonctions avec des effets de bord (lecture de fichiers / écriture en BD / retour de l'heure courante) ou dont le résultat varie pour les mêmes entrées, et tu auras un bug où le premier résultat reviendra pour toujours. Applique-le seulement aux fonctions pures (même entrée → même sortie, pas d'effet de bord).
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Que retourne partial(f, x=10) ?
Question 3Pourquoi @lru_cache accélère-t-il un Fibonacci récursif naïf ?