Le fait qu’un programme marche n’est pas suffisant voire parfois “secondaire” !
Autrement dit : la lisibilité pour vous et vos collègues a énormément d’importance pour la maintenabilité et l’évolution d’un projet
On écrit pas un programme qui marche au premier essai
pdb
, ipdb
, VSCodePDB = Python DeBugger
Permet (entre autre) de définir des “break points” pour rentrer en interactif
import ipdb; ipdb.set_trace()
breakpoint()
Mais fait appel à pdb
et non ipdb
?Une fois en interactif, on peut inspecter les variables, tester des choses, …
On dispose aussi de commandes spéciales pour executer le code pas-à-pas
Significativement plus efficace que de rajouter des print()
un peu partout !
l(ist)
: affiche les lignes de code autour de code (ou continue le listing precedent)
c(ontinue)
: continuer l’execution normalement (jusqu’au prochain breakpoint)
s(tep into)
: investiguer plus en détail la ligne en cours, possiblement en descendant dans les appels de fonction
n(ext)
: passer directement à la ligne suivante
w(here)
: print the stack trace, c.a.d. les différents sous-appels de fonction dans lesquels on se trouve
u(p)
: remonte d’un cran dans les appels de la stacktrace
d(own)
: redescend d’un cran dans les appels de la stacktrace
b(reak)
: fixe un point d’arrêt (breakpoint) à la ligne donnée.
tbreak
: fixe un point d’arrêt temporaire qui sera retiré au premier passage.
pp <variable>
: pretty-print d’une variable (par ex. une liste, un dict, ..)
Keep It Simple
Sémantique : utiliser des noms de variables et de fonctions qui ont du sens
Architecture : découper son programme en fonction qui chacune résolvent un sous-problème précis
Robustesse : garder ses fonctions autant que possibles indépendantes, limiter les effets de bords
Lorsque mon programme évolue, je prends le temps de le refactoriser si nécessaire
La célèbre suite de Fibonacci, liée au nombre d’or, est une suite d’entiers dans laquelle chaque terme est la somme des deux termes qui le précèdent. Mais elle est également un exercice classique d’algorithmique.
Écrire une fonction fibonacci_rec_naive(n)
qui calcule de façon récursive la suite de fibonacci.
Créez une autre fonction fibonacci_iter(n)
qui calcule de façon iterative la suite de fibonacci.
Calculez le 40e terme de la suite avec chacune des implémentation précédente.
Debuggez les deux implémentations. Que se passe-t-il ?
A l’aide de la librairie timeit et de sa fonction timer (from timeit import default_timer as timer
) qui renvoie le temps processeur courant, mesurez le temps d’exécution des deux fonctions.
Écrire une fonction fibonacci_rec_liste(n)
qui calcule récursivement la suite de fibonacci en utilisant une liste comme mémoire pour ne pas recalculer les terme déjà calculés.
Bonus 1: Utilisons un décorateur de “caching” de fonction (from functools import lru_cache as cache
) sur fibonacci_rec_naive(n)
pour l’optimiser sans changer le code.
Bonus 2: Écrivons une implémentation pythonique de fibonacci utilisant un générateur