- Langages et programmation -
Gestion des bugs
Avant de recenser les erreurs, commençons par apprendre à les « lire ».
Les lignes ci-dessous sont adaptées de ce post de forum.
Lorsqu’un programme Python plante, c’est en général (si ce n’est toujours…) en raison d’une exception non gérée. Voici le genre d’affichage que cela produit dans la console :
Traceback (most recent call last):
File "test.py", line 6, in <module>
test()
File "test.py", line 3, in test
print table[4]
IndexError: list index out of range
Ces erreurs affichées par Python se lisent de bas en haut. La ligne la plus importante est donc la dernière !
IndexError: list index out of range
Nous y trouvons deux informations :
IndexError
Les lignes qui précèdent la dernière sont ce que l’on appelle le traceback. C’est ce qui permet de trouver rapidement où s’est produite l’erreur. Ces lignes vont en général 2 par 2 :
File "test.py", line 3, in test
print table[4]
La première ligne indique :
La seconde ligne présente l’instruction qui a fait planter le programme.
Les paires de lignes qui précèdent celle-ci sont les appels de fonctions successifs qui ont mené l’interpréteur jusqu’à cette instruction. Cela permet de savoir grosso-modo où en était votre programme dans son exécution avant qu’il ne plante.
Cette section est bien entendue non exhaustive !
Tout d’abord un code générant cette erreur :
L’erreur vient du fait que le sous-bloc (à l’intérieur du if
) doit être indenté, décalé. L’usage veut que l’on utilise soit quatre espaces soit une tabulation.
Le même code corrigé :
Code générant l’erreur :
Il s’agit le plus souvent d’une faute de frappe : ici on a tapé form
au lieu de from
.
Le même code corrigé :
Code générant l’erreur :
La variable b
n’a pas encore été définie ! Python le signale.
Le même code corrigé :
Code générant l’erreur :
Le module_inexistant
n’est pas identifié par Python. L’erreur peut provenir d’une faute de frappe lors de la saisie de son nom (courant) ou d’un problème d’installation. Il faut dans ce cas vérifier le path
de python.
Ici on demande à Python d’afficher le 3ème élément de a
(d’indice 2
). Or a
ne possède que 2 éléments : on obtient une erreur d’indice. Il faut vérifier nos appels :
La fonction sqrt
prend en argument des nombres or ici on lui propose une chaîne de caractère… On peut facilement éviter ce genre d’erreur lisant la documentation de la fonction (avec help
par exemple) :
In [1]: help(sqrt)
Help on built-in function sqrt in module math:
sqrt(x, /)
Return the square root of x.
Dans certains cas, un programme n’effectue pas ce que l’on souhaite sans que Python ne détecte d’erreur. Il est donc plus compliqué de trouver la source du problème. Voici quelques cas à envisager.
Observons le code ci-dessous :
Les conditions sont mal formulées, l’exécution ne fournira aucun affichage ! Il faudrait plutôt avoir :
Il peut arriver qu’une instruction modifie des variables annexes sans que l’on s’en rende compte. C’est souvent le cas lorsque l’on utilise des listes :
In [1]: liste = [[0] * 3] * 2
In [2]: liste
Out[2]: [[0, 0, 0], [0, 0, 0]]
In [3]: liste[0][1] = "ici"
In [4]: liste
Out[4]: [[0, 'ici', 0], [0, 'ici', 0]]
Dans cet exemple, on crée une liste
contenant deux fois la sous-liste [0, 0, 0]
. Dans l’instruction de la ligne 3, on modifie le deuxième élément de la première sous-liste.
On constate dans la ligne 4 que les deux sous-listes ont été modifiée. En effet, pour Python, ces deux sous-listes sont un seul et même objet comme le montre la fonction id
qui donne l’identifiant de l’object :
In [5]: id(liste[0])
Out[5]: 1482323031432
In [6]: id(liste[1])
Out[6]: 1482323031432
La méthode la plus efficace, mais radicale, pour éviter les effets de bords est d’utiliser la programmation fonctionnelle
Python, à l’instar de tous les langages de programmation, code les nombres réels sous forme binaire. Or les seuls nombres pouvant s’écrire parfaitement (sans une partie décmale infinie) sous forme binaire sont ceux de la forme \(\frac{n}{2^k}\) (un entier divisé par une puissance de \(2\)).
On arrive facilement à des erreurs :
In [1]: 1-1/3-2/3
Out[1]: 1.1102230246251565e-16
Ici \(\frac{1}{3}\) est mal codé par Python, donc le calcul est faux.
Il ne faut donc jamais tester l’égalité de nombres flottants, les erreurs de calculs peuvent engendrer des erreurs :
In [2]: a = 1
In [3]: while a != 0 :
...: a = a - 0.1
...: print(a)
0.9
0.8
0.7000000000000001
0.6000000000000001
0.5000000000000001
0.40000000000000013
0.30000000000000016
0.20000000000000015
0.10000000000000014
1.3877787807814457e-16
-0.09999999999999987
Ici, \(0,1\) ne s’écrie par correctement en python donc on n’a pas \(1 - 10 \times 0,1 = 0\).
Une bonne façon de gérer ces cas de figures est :
In [4]: a = 1
In [5]: while a > 0 :
...: a = a - 0.1
...: print(a)
0.9
0.8
0.7000000000000001
0.6000000000000001
0.5000000000000001
0.40000000000000013
0.30000000000000016
0.20000000000000015
0.10000000000000014
1.3877787807814457e-16
-0.09999999999999987