La suite de Fibonacci est un petit exercice qui en dit beaucoup sur la façon d’écrire du Python propre: boucle, récursion, générateur, cache et gestion des limites réelles du code. Ici, je vais montrer les approches utiles pour générer la suite de Fibonacci en Python, expliquer quand chacune est pertinente et éviter les pièges classiques. Si vous voulez aller au-delà d’un simple exemple de manuel, c’est exactement le bon terrain.
Les points à retenir avant d’écrire le code
- Pour produire une suite finie, la boucle reste la solution la plus lisible et la plus robuste.
- Pour consommer les valeurs une par une, un générateur est plus propre qu’une liste complète.
- La récursion pure est élégante mais devient vite trop coûteuse.
- Avec `functools.lru_cache`, la récursion devient acceptable pour des tailles modestes.
- En Python, la taille des entiers augmente vite, donc le coût réel n’est pas seulement algorithmique.
Pourquoi la suite de Fibonacci reste un bon exercice Python
J’aime cet exemple parce qu’il révèle vite deux choses: la capacité à écrire une logique simple et claire, et la discipline nécessaire pour ne pas surcompliquer un problème qui tient en quelques lignes. Fibonacci sert souvent de test de lecture du code, pas seulement de test de syntaxe.
Ce n’est pas un problème théorique. Dès qu’on choisit entre une liste, un flux de valeurs ou une fonction qui ne renvoie qu’un terme, on touche à des questions très concrètes: mémoire, lisibilité et coût algorithmique. C’est précisément ce qui permet de choisir ensuite entre une liste, un générateur ou une version récursive.

La version la plus lisible avec une boucle
Je pars ici sur la convention la plus courante dans un code d’exemple: 0, 1, 1, 2, 3, 5.... Si votre projet démarre à 1, 1, adaptez simplement les valeurs initiales. Je préfère cette version quand je veux une suite finie prête à être affichée, testée ou envoyée vers une autre fonction.
def suite_fibonacci(n):
if n <= 0:
return []
if n == 1:
return [0]
suite = [0, 1]
while len(suite) < n:
suite.append(suite[-1] + suite[-2])
return suite
print(suite_fibonacci(10))Cette forme est facile à relire et assez robuste pour un débutant comme pour un code de production. Je n’utilise pas de compréhension de liste ici: la dépendance au terme précédent mérite un état explicite, pas une astuce compacte qui brouille la lecture. Quand on n’a pas besoin de tout conserver, le générateur devient alors plus intéressant.
Générer les termes à la volée avec un générateur
Un générateur est utile quand on veut produire les valeurs à la demande. On ne fabrique pas toute la liste d’un coup: chaque appel à `yield` renvoie un terme, puis la fonction reprend là où elle s’était arrêtée. C’est plus naturel pour un pipeline de données, un affichage progressif ou un traitement qui s’arrête avant le n-ième terme.
from itertools import islice
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
print(list(islice(fibonacci(), 10)))Je choisis cette approche quand je veux consommer la suite sans l’immobiliser en mémoire. Elle est particulièrement propre dans un script qui alimente un autre traitement, parce qu’elle garde le flux de valeurs ouvert au lieu de tout matérialiser d’un coup. Si votre but est seulement d’obtenir un terme précis, la récursion avec cache peut encore avoir du sens, à condition de la cadrer.
Récursion et mémoïsation quand vous voulez un exemple plus sophistiqué
La récursion pure est séduisante, mais je la réserve surtout à l’explication pédagogique. Sans mémoïsation, elle recalcule les mêmes sous-problèmes des dizaines de fois et devient vite disproportionnée. Avec un cache, elle redevient utilisable pour des tailles modestes, même si elle reste moins directe qu’une boucle pour produire toute la suite.
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
def suite_fibonacci(n):
return [fib(i) for i in range(n)]
print(suite_fibonacci(10))Cette version a un intérêt précis: elle illustre bien la mémoïsation, c’est-à-dire le fait de mémoriser les résultats déjà calculés pour éviter les doublons. Sur un Python récent, `functools.cache` peut aussi faire le travail si vous n’avez pas besoin d’une limite de taille. Pour produire toute la suite, je garde toutefois une préférence nette pour la boucle ou le générateur.
Même avec ces trois formes, la vraie question reste le coût réel selon l’usage. C’est ce que je compare juste après.
Comparer les approches sans se tromper sur le coût
Le choix n’est pas seulement stylistique. En Python, les entiers grandissent sans limite pratique, mais leur addition coûte de plus en plus cher quand les termes deviennent énormes. Sur dix ou vingt valeurs, on ne voit presque rien; sur des indices élevés, c’est la taille des nombres qui finit par peser autant que l’algorithme. C’est pour cela que je déconseille la formule fermée basée sur les flottants pour du code exact: elle semble élégante, puis elle finit par trahir la précision.
| Approche | Temps | Mémoire | Forces | Limites |
|---|---|---|---|---|
| Boucle | O(n) | O(n) | Lisible, simple à déboguer, bon choix par défaut | Stocke toute la liste si vous la renvoyez telle quelle |
| Générateur | O(n) | O(1) | Idéal pour traiter les valeurs une par une | Moins pratique si vous voulez réutiliser toute la suite plusieurs fois |
| Récursion pure | O(phi^n) | O(n) | Bonne démonstration pédagogique | Trop lente dès que n grandit |
| Récursion avec cache | O(n) | O(n) | Utile pour illustrer la mémoïsation ou calculer un terme isolé | Moins directe qu’une boucle pour produire une suite complète |
Ce tableau résume bien mon arbitrage: si je veux une séquence concrète, je prends la boucle; si je veux un flux, je prends le générateur; si je veux expliquer le cache, je prends la récursion mémoïsée. À partir de là, le bon choix dépend moins de la théorie que du contexte du script.
Ce que je retiendrais avant de l’intégrer à un vrai script
Avant de reprendre ce code dans un projet, je vérifie toujours trois choses: la convention de départ, le mode de consommation des valeurs et le volume réel de données à produire. Une suite qui sert à afficher dix termes n’a pas les mêmes exigences qu’un traitement qui alimente une API, un notebook ou un job batch.
- Si vous voulez une suite finie pour l’affichage ou les tests, la boucle reste la meilleure base.
- Si vous devez chaîner des opérations sans tout stocker, le générateur est plus naturel.
- Si vous avez besoin du n-ième terme plusieurs fois, le cache devient intéressant.
- Si vous poussez n très haut, regardez les méthodes logarithmiques comme le fast doubling plutôt que d’insister avec une version pédagogique.
En pratique, je garde trois réflexes: une convention de départ explicite, un choix adapté à la quantité de données, et un œil sur la mémoire si la suite sort d’un simple exercice. C’est ce qui transforme un exemple Fibonacci en code Python vraiment utile, pas seulement en démonstration correcte.