I. Présentation▲
Ce tutoriel n'a pas la prétention de couvrir toute la discipline du débogage, il vise juste à apporter des méthodes simples, rapides et faciles à mémoriser, que tout un chacun pourra s'approprier pour ne pas perdre un temps précieux.
II. Déboguer sans IDLE▲
Avant de sortir l'artillerie lourde pour dégommer un malheureux puceron, il peut être utile d'envisager quelques méthodes simples et rapides.
II-A. Déboguer avec Traceback▲
Les messages d'erreur de Python - dits Tracebacks - sont souvent passablement fournis et peuvent en décourager plus d'un. Pourtant, leur richesse est justement une mine d'information qui peut vous faire gagner un temps appréciable.
N'hésitez pas à consulter régulièrement la documentation officielle Python concernant les exceptions (types d'erreur).
Voici une méthode simple pour s'y retrouver :
- lire la dernière ligne en premier : lisez toujours un message d'erreur à partir de la dernière ligne, puis remontez à rebours les lignes tant que vous n'avez pas trouvé un indice suffisant pour vous mettre sur la bonne piste ;
- comprendre le message d'erreur : cela semble tomber sous le sens et pourtant, plus d'un message d'erreur vous paraîtra étrangement obscur ; si en outre votre anglais est approximatif, mieux vaut demander à une personne qui comprend cette langue de venir vous aider à décrypter le message d'erreur ;
- chercher les indications de localisation : cherchez aussi les indices qui vous permettent de localiser l'erreur : quel fichier ? Quelle ligne ? Quelle fonction / méthode ?
- aller voir le code source : n'hésitez pas à vous rendre au chevet du code source selon les indications fournies par le Traceback, il se peut qu'une fois sur place, d'autres lignes de code vous aiguillent dans votre diagnostic ;
-
lire la documentation : beaucoup d'erreurs sont commises faute de recherche documentaire suffisante sur les classes, objets, méthodes et fonctions employées dans le code ; les documentations officielles sont là pour ça : une petite requête dans votre moteur de recherche préféré et vous voilà parti(e) à la conquête de l'API ultime ;
Quelques documentations incontournables à enregistrer dare-dare dans vos favoris :
documentation officielle Python2 & Python3, fonctions internes Python2 & Python3, modules Python2 & Python3, documentation officielle Tkinter, documentation officielle ttk, documentation geometry managers pack(), grid() et place(), PIL Image module, Tkinter Color Names, conventions de nommage PEPPython Enhancement Proposals - Propositions d'amélioration de Python 8, encodage des scripts PEP 0263 et Python2 Unicode HOWTO.
-
tester dans une console : dans certains cas, des expressions simples pourraient cacher des bogues particulièrement subtils ; un bon test de valeurs à la console Python permettrait de lever les doutes ;
-
tester sur un coin de table : il peut arriver qu'un problème soit très localisé au milieu d'un océan de plusieurs milliers de lignes de code ; dans ce cas, il sera préférable de se créer un fichier de test à part e.g. test.py puis d'y copier/coller uniquement la portion de code qui nous intéresse, pour pouvoir ensuite résoudre le problème en ne se focalisant que sur la zone incriminée ; pour en savoir plus, jetez un œil du côté de la rubrique Faire un test « sur un coin de table » ;
- demander sur un forum d'entraide : si vraiment vous n'y arrivez pas, n'hésitez pas à poster votre demande sur le forum adéquat, accompagnée du code source en question ainsi que d'une copie fidèle - et complète - du message d'erreur concerné ; dans ce cas, merci de prendre connaissance de quelques règles avant de poster votre requête ;
Pour publier du code source :
Voici un exemple de bogue courant :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import
random
def
verifier_numero_gagnant (
x):
if
x +
3
==
10
:
print
"Bravo ! C'est gagné !"
else
:
print
"Désolé ! Réessayez une nouvelle fois..."
# end if
# end def
# test
chiffres =
list(
'1234567890'
)
random.shuffle
(
chiffres)
verifier_numero_gagnant
(
chiffres[3
])
Ce code produit le message d'erreur Traceback suivant :
Traceback (most recent call last):
File "./mon_script.py", line 17, in <module>
verifier_numero_gagnant(chiffres[3])
File "./mon_script.py", line 7, in verifier_numero_gagnant
if x + 3 == 10:
TypeError: cannot concatenate 'str' and 'int' objects
En procédant dans l'ordre, commençons par lire la dernière ligne du message :
TypeError: cannot concatenate 'str' and 'int' objects
Que signifie ce message ?
Allons voir dans la documentation officielle ce qui se dit à propos de l'exception TypeError.
Extrait : « Raised when an operation or function is applied to an object of inappropriate type. The associated value is a string giving details about the type mismatch. »
Pas assez parlant.
Lisons les autres lignes du Traceback, à rebours :
File "./mon_script.py", line 7, in verifier_numero_gagnant
if x + 3 == 10:
Que peut-on extraire comme information de cette phrase ?
On nous dit que ça se passe dans le fichier mon_script.py, à la ligne 7, dans la fonction verifier_numero_gagnant et que ça concerne la ligne de code if x + 3 == 10:.
Allons voir ligne 7 de ce fichier :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import
random
def
verifier_numero_gagnant (
x):
if
x +
3
==
10
:
print
"Bravo ! C'est gagné !"
else
:
print
"Désolé ! Réessayez une nouvelle fois..."
# end if
# end def
A priori, une expression telle que x + 3 == 10 ne semble pas particulièrement erronée.
Cette ligne est correctement écrite, le problème ne doit pas venir de là.
Remontons encore le Traceback :
File "./mon_script.py", line 17, in <module>
verifier_numero_gagnant(chiffres[3])
Apparemment, le problème viendrait de chiffres[3].
Que peut bien contenir chiffres[3] ?
Ouvrons une console Python et testons :
>>> import random
>>> chiffres = list('1234567890')
>>> random.shuffle(chiffres)
>>> chiffres
['8', '7', '9', '6', '4', '0', '1', '2', '3', '5']
>>> chiffres[3]
'6'
>>> 6 + 3 == 10
False
>>> '6' + 3 == 10
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
'6' + 3 == 10
TypeError: cannot concatenate 'str' and 'int' objects
Note : les symboles '>>> ' représentent l'invite de commande de la console Python et ne doivent pas être saisis au clavier.
N'entrez que ce qui suit ces symboles.
Boum ! La même erreur que dans notre message de Traceback précédent.
Visiblement, l'erreur commise ici est de passer une chaîne de caractères contenant un chiffre en guise d'argument de fonction à verifier_numero_gagnant(x) au lieu de lui passer un nombre entier.
Pour corriger le problème, nous pourrions, par exemple, nous abstenir de passer autre chose qu'un entier naturel à la fonction verifier_numero_gagnant(x), ce qui serait logique puisque comme son nom l'indique, cette fonction attend un nombre et non pas une chaîne de caractères, quand bien même cette dernière contiendrait un nombre à l'intérieur, cela ne changerait rien.
Redéfinissons notre liste de chiffres avec quelque chose de plus cohérent :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import
random
def
verifier_numero_gagnant (
x):
if
x +
3
==
10
:
print
"Bravo ! C'est gagné !"
else
:
print
"Désolé ! Réessayez une nouvelle fois..."
# end if
# end def
# test
chiffres =
range(
10
)
random.shuffle
(
chiffres)
verifier_numero_gagnant
(
chiffres[3
])
Qui produit dès lors le résultat console suivant :
Bravo ! C'est gagné !
Ça fonctionne.
Le bogue semble corrigé.
Conclusion : même si l'on ne comprend pas forcément très bien l'anglais, il y a toujours moyen d'y arriver, simplement en réanalysant le code selon les indications du Traceback.
II-B. Déboguer avec assert▲
Il existe une alternative à la méthode « Déboguer avec Traceback » : utiliser le mot-clé réservé du langage Python assert.
Pour en savoir plus : assert statement (documentation officielle, en anglais).
Une expression 'assert condition' permet de lever une exception AssertionError si la condition mentionnée ne se vérifie pas.
Exemple :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import
random
def
verifier_numero_gagnant (
x):
# on attend un entier naturel ici
# lèvera une exception AssertionError
# si x n'est pas un entier
assert
type(
x) is
int
if
x +
3
==
10
:
print
"Bravo ! C'est gagné !"
else
:
print
"Désolé ! Réessayez une nouvelle fois..."
# end if
# end def
# test
chiffres =
list(
'1234567890'
)
random.shuffle
(
chiffres)
verifier_numero_gagnant
(
chiffres[3
])
Qui produit comme résultat :
Traceback (most recent call last):
File "./mon_script.py", line 21, in <module>
verifier_numero_gagnant(chiffres[3])
File "./mon_script.py", line 10, in verifier_numero_gagnant
assert type(x) is int
AssertionError
Conclusion : x n'est pas un entier en entrant dans la fonction, il faut remédier à cela (voir le corrigé dans « Déboguer avec Traceback »).
Le mot-clé assert est une facilité de débogage, il correspond peu ou prou à la définition canonique :
if
not
expression: raise
AssertionError
Ici, la logique veut que l'on affirme ce que l'on attend dans 'assert expression' plutôt que de chercher soi-même des valeurs susceptibles de causer l'erreur dans un test en console - comme nous l'avons fait dans « Déboguer avec Traceback ».
Chaque méthode a ses avantages et ses inconvénients, mais rien n'empêche de recourir à un mix des deux pour se faciliter la tâche.
II-C. Faire un test « sur un coin de table »▲
Lorsqu'un projet prend suffisamment d'ampleur pour compter plusieurs milliers de lignes de code, il peut arriver qu'un problème ne soit localisé qu'à une toute petite portion de ce code.
Dans ce cas, le mieux reste encore de se créer un fichier de test à part e.g. test.py, dans lequel on copiera et collera la portion concernée pour ensuite tracer le problème isolément.
Pour les projets vraiment imposants, pensez à créer un fichier de test à part puis à copier/coller la portion de code qui pose problème dans ce fichier pour faciliter vos recherches.
Une fois cette opération effectuée, vous aurez toute liberté d'opter pour l'une ou l'autre des méthodes de débogage suivantes :
- Déboguer avec Traceback ;
- Déboguer avec assert ;
- Déboguer avec IDLE ;
- Déboguer avec vos propres méthodes.
Bien entendu, une fois le problème identifié et corrigé, il ne vous restera plus qu'à transposer le code en retour dans votre projet d'origine.
III. Déboguer avec IDLE▲
Découvrez IDLE dans la documentation officielle Python.
Si aucune des méthodes précédentes ne vous apporte entière satisfaction, peut-être est-il temps pour vous de vous pencher sur les fonctionnalités de débogage du logiciel IDLE conçu pour le langage Python que vous utilisez ?
Dans la suite de ce tutoriel, nous nous baserons sur la version IDLE pour Python2, sachant toutefois que vous pouvez aisément tout transposer pour IDLE3 (Python3), les fonctionnalités étant les mêmes d'une version à l'autre.
III-A. Mode normal▲
Si vous ne connaissez pas vraiment IDLE, merci de lire tout d'abord l'article intitulé « IDLE - présentation rapide ».
III-A-1. Préparatifs▲
Pour illustrer le propos, utilisons le code source suivant :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def
f
(
x):
if
x %
4
==
0
:
a =
10.584
elif
x %
4
==
1
:
a =
12.352
elif
x %
4
==
2
:
a =
24.689
elif
x %
4
==
3
:
a =
39.547
# end if
# on retourne le résultat de notre traitement
return
a
# end def
# test
x =
100.235
print
"ajustement de la valeur de {} : {}"
.format
(
x, f
(
x))
L'exécution de ce code (touche F5) produit l'erreur suivante :
Traceback (most recent call last):
File "/home/rs/Documents/DVP/tutoriel-deboguer-avec-IDLE/fichiers/python2-erreur-unboundlocalerror.py", line 22, in <module>
print "ajustement de la valeur de {} : {}".format(x, f(x))
File "/home/rs/Documents/DVP/tutoriel-deboguer-avec-IDLE/fichiers/python2-erreur-unboundlocalerror.py", line 15, in f
return a
UnboundLocalError: local variable 'a' referenced before assignment
III-A-2. Utiliser Go to File/Line▲
Dans un premier temps, nous allons simplement exploiter l'option de menu Go to File/Line afin de voir où l'erreur s'est produite.
Pour cela, cliquez simplement sur l'avant-dernière ligne de Traceback comme illustré ci-dessous.
Puis sélectionnez le menu Debug, option Go to File/Line.
Cette option de menu va vous montrer, dans la fenêtre Script, la ligne de code désignée par le message de Traceback.
En observant le code qui précède la ligne désignée en surbrillance, on s'aperçoit que la fonction f(x) est essentiellement composée d'une batterie de tests basée sur l'expression x % 4 que l'on compare successivement aux entiers 0, 1, 2, et 3, étant donné que par définition 0 ≤ x % 4 < 4.
III-A-3. Explorer avec Stack Viewer▲
Voyons si la fenêtre Stack Viewer peut nous être d'un quelconque secours.
Sélectionnez le menu Debug, option Stack Viewer.
Ne lancez jamais la fenêtre Stack Viewer pendant une session de débogage active - c'est-à-dire quand la fenêtre Debugger est en cours d'utilisation, vous provoqueriez un plantage définitif du logiciel IDLE, avec l'obligation ensuite de recourir au gestionnaire de tâches de votre système pour tuer le processus gelé.
La fenêtre Stack Viewer s'ouvre et affiche les données suivantes.
Déplions le dernier nœud d'arborescence et jetons-y un œil.
Bon, on remarque ici la valeur du paramètre x = 100.235 dans <locals>, mais même en fouillant un peu plus dans les autres nœuds d'arborescence, il semblerait que Stack Viewer ne nous soit pas d'un grand secours pour le cas de figure ici présent.
III-A-4. Tester dans la console interactive Python d'IDLE▲
Faisons quelques tests dans la console interactive de la fenêtre Shell d'IDLE : prenons x = 100.235 et passons-le manuellement à la batterie de tests de la fonction f(x).
Bien, le constat est sans appel : nous obtenons x % 4 = 0.23499999999999943 qui n'est pas un nombre entier comme la batterie de tests l'attend en réalité.
Nous avons donc bien un bogue dans la fonction f(x) puisque la variable locale 'a' ne sera jamais initialisée pour un paramètre x non entier.
III-A-5. Corriger l'erreur▲
Corrigeons cette erreur en forçant notre paramètre x à adopter sa partie entière uniquement.
Nous obtenons donc le code source final :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def
f
(
x):
# on veut x un entier
x =
int(
x)
# le reste devrait suivre à présent
if
x %
4
==
0
:
a =
10.584
elif
x %
4
==
1
:
a =
12.352
elif
x %
4
==
2
:
a =
24.689
elif
x %
4
==
3
:
a =
39.547
# end if
# on retourne le résultat de notre traitement
return
a
# end def
# test
x =
100.235
print
"ajustement de la valeur de {} : {}"
.format
(
x, f
(
x))
On lance le script (touche F5) pour vérifier si tout est OK.
III-A-6. Conclusion▲
Notre bogue est tracé, trouvé et fixé, donc c'est tout bon.
III-B. Mode débogage▲
Si vous ne connaissez pas vraiment IDLE, merci de lire tout d'abord l'article intitulé « IDLE - présentation rapide ».
III-B-1. Préparatifs▲
Voyons à présent comment nous aurions pu résoudre le même problème avec le débogueur intégré d'IDLE.
Reprenons le code source de départ :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def
f
(
x):
if
x %
4
==
0
:
a =
10.584
elif
x %
4
==
1
:
a =
12.352
elif
x %
4
==
2
:
a =
24.689
elif
x %
4
==
3
:
a =
39.547
# end if
# on retourne le résultat de notre traitement
return
a
# end def
# test
x =
100.235
print
"ajustement de la valeur de {} : {}"
.format
(
x, f
(
x))
Et toujours son erreur :
Traceback (most recent call last):
File "/home/rs/Documents/DVP/tutoriel-deboguer-avec-IDLE/fichiers/python2-erreur-unboundlocalerror.py", line 22, in <module>
print "ajustement de la valeur de {} : {}".format(x, f(x))
File "/home/rs/Documents/DVP/tutoriel-deboguer-avec-IDLE/fichiers/python2-erreur-unboundlocalerror.py", line 15, in f
return a
UnboundLocalError: local variable 'a' referenced before assignment
À présent, passons en mode débogage.
III-B-2. Fenêtre Debugger▲
Sélectionnez le menu Debug, option Debugger.
Une fenêtre Debugger s'ouvre et la console interactive Python de la fenêtre Shell passe en mode [DEBUG ON].
Cochez toutes les cases à cocher de la fenêtre Debugger.
À partir de maintenant, si nous lançons simplement notre script (touche F5), c'est la fenêtre Debugger qui prendra la main sur le déroulement de l'exécution du programme.
III-B-3. Fonctionnalités du débogueur▲
Étudions la fonctionnalité de chaque bouton cliquable de cette fenêtre spéciale :
- Go : parcourt le code jusqu'au prochain point d'arrêt (breakpoint) ou la prochaine saisie clavier dans la console (input) ou jusqu'à la fin du programme ;
- Step : parcourt le code pas à pas, une ligne après l'autre ; si une fonction est appelée en cours de route, parcourt aussi le détail de la fonction ; passe absolument partout ;
- Over : parcourt le code pas à pas en sautant les appels de fonction (sauf si un point d'arrêt y est placé) ; parcourt en quelque sorte les lignes générales du programme ;
- Out : permet de sortir d'une fonction et de retourner à l'appelant ;
- Quit : quitte la session de débogage actuelle sans fermer la fenêtre Debugger ni sortir du mode [DEBUG ON] ; il faut fermer la fenêtre Debugger manuellement si l'on veut quitter définitivement le mode débogage i.e. [DEBUG OFF].
Toutes les fonctionnalités de parcours de code s'arrêtent toujours au prochain point d'arrêt (breakpoint) rencontré sur leur chemin d'exécution.
III-B-4. Premiers essais▲
Dans un premier temps, cliquez sur le bouton Step pour voir comment la fenêtre Debugger gère le parcours du programme pas à pas.
Avancez pas à pas dans le programme jusqu'à entrer dans la fonction f(x) comme illustré ci-dessus.
Cliquez ensuite sur le bouton Out pour sortir de la fonction et retourner à la ligne appelante.
Pour finir, cliquez sur le bouton Go pour achever le déroulement du programme.
Si vous souhaitez arrêter votre session de débogage en cours de route, cliquez sur le bouton Quit et ensuite seulement, fermez la fenêtre Debugger.
Pour la suite du tutoriel, laissez la fenêtre Debugger ouverte pour le moment.
III-B-5. Points d'arrêt (breakpoints)▲
Placez un point d'arrêt (breakpoint) dans le code, à présent.
Faites un clic droit sur la ligne de code if x % 4 == 0: puis sélectionnez Set Breakpoint dans le menu contextuel qui apparaît :
La ligne de code est désormais surlignée en jaune.
Vous pouvez placer autant de points d'arrêt que vous le souhaitez pour vous aider dans votre recherche.
Lancez le script avec la touche F5, puis cliquez cette fois sur le bouton Go : vous devriez arriver directement sur le point d'arrêt que vous venez de placer dans le code.
À partir de là, vous pouvez avancer pas à pas avec le bouton Step ou le bouton Over, selon ce que vous recherchez dans le déroulement de l'exécution du code.
Essayez d'exploiter au maximum toutes les informations que la fenêtre Debugger vous fournit à chaque étape d'analyse.
Lorsque votre recherche prend fin, cliquez sur le bouton Quit et ensuite seulement, fermez la fenêtre Debugger.
La console interactive Python de la fenêtre Shell d'IDLE indiquera alors [DEBUG OFF] pour marquer la fin de la session de débogage.
Pour terminer, supprimez vos points d'arrêt devenus inutiles, en faisant un clic droit sur les lignes de code surlignées en jaune et en sélectionnant Clear Breakpoint dans le menu contextuel qui apparaît.
III-B-6. Conclusion▲
Voilà.
Pour le reste, déboguer avec IDLE ne sera plus qu'une question d'expérience, de temps et de patience.
IV. Ces bogues qui gagnent à être connus▲
N'hésitez pas à consulter régulièrement la documentation officielle Python concernant les exceptions (types d'erreur).
IV-A. Syntax Error sur une ligne correcte▲
C'est une erreur qui en déroute plus d'un lorsqu'elle apparaît pour la première fois.
Extrait : « exception SyntaxError : raised when the parser encounters a syntax error. This may occur in an import statement, in a call to the built-in functions exec() or eval(), or when reading the initial script or standard input (also interactively). »
L'interpréteur Python peut lever une exception SyntaxError et indiquer une ligne de code parfaitement correcte s'il détecte une incohérence syntaxique dans une expression qui généralement prend sa source quelques lignes plus haut.
Pensez à la parenthèse fermante certainement oubliée à la ligne précédente.
C'est le cas typique d'une parenthèse fermante, d'un crochet fermant ou d'une accolade fermante que l'on oublie dans les lignes qui précèdent l'indication de l'erreur dans le message de Traceback.
La méthode consiste ici à reprendre le code à l'endroit indiqué par le message d'erreur puis à remonter à rebours les lignes jusqu'à trouver ce qui manque - comme une parenthèse fermante, par exemple - ou ce qui ne va pas.
Exemple :
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
x =
2
**
3
+
3
**
3
+
4
**
3
+
5
**
3
print
(
"somme des cubes :"
, x # <-- ERREUR parenthèse manquante
x =
1
+
2
+
3
+
4
+
5
print
(
"somme des termes :"
, x)
Ce code génère l'erreur suivante :
File "./forum.py", line 8
x = 1 + 2 + 3 + 4 + 5
^
SyntaxError: invalid syntax
Ici, le message d'erreur désigne une ligne de code parfaitement correcte, alors que l'erreur se situe pour sa part dans la ligne précédente.
IV-B. NameError: name '…' is not defined▲
Il s'agit d'une erreur classique : on fait appel à un identificateur avant de l'avoir défini.
Extrait : « exception NameError : raised when a local or global name is not found. This applies only to unqualified names. The associated value is an error message that includes the name that could not be found. »
En revanche, les sources d'erreur sont multiples :
- vous avez fait une faute de frappe - vous avez tapé 'dimetre_cercle' au lieu de 'diametre_cercle', par exemple ;
- vous avez fait une erreur de casse de caractères (majuscules / minuscules) - exemple : 'lframe = tk.Labelframe(root)' au lieu de 'lframe = tk.LabelFrame(root)' ;
- vous avez réellement oublié d'initialiser la variable ou la fonction/méthode avant de vous en servir.
IV-C. UnboundLocalError: local variable '…' referenced before assignment▲
Il s'agit d'une erreur plus subtile que la NameError classique.
Ici, on fait appel à une variable locale qui n'a été définie que dans un bloc if … : … else : … mais dont aucune condition de test ne se vérifie : la variable existe mais n'a pas été initialisée correctement.
Extrait : « exception UnboundLocalError : raised when a reference is made to a local variable in a function or method, but no value has been bound to that variable. This is a subclass of NameError. »
La solution la plus courante consiste à initialiser les variables locales en tout début de définition de fonction et en-dehors de tout contexte conditionnel incertain.
Exemple :
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
def
f
(
x):
# on initialise en début
# de définition de fonction
a =
0
# si le test échoue...
if
x >
10
:
a =
x /
10
# end if
# ... la locale 'a'
# sera quand même initialisée
return
a
# end def
# essai
print
(
f
(
3
))
Il s'agit généralement d'une erreur d'inattention ou d'un excès de confiance dans les tests effectués.
Exemple typique :
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
def
f
(
x):
# si le test échoue...
if
x >
10
:
a =
x /
10
# end if
# ... la locale 'a'
# ne sera pas initialisée
return
a
# end def
# essai
print
(
f
(
3
))
Ce code lève l'exception suivante :
Traceback (most recent call last):
File "./mon_script.py", line 16, in <module>
print(f(3))
File "./mon_script.py", line 11, in f
return a
UnboundLocalError: local variable 'a' referenced before assignment
Tout simplement parce que la condition établie dans la fonction ne suffit pas à une initialisation correcte de la variable 'a'.
Conclusion : le mieux dans ce cas reste encore bien d'initialiser les variables locales en tout début de définition de fonction.
V. Remerciements▲
Je tiens à remercier chaleureusement toutes les équipes du site Développez.com pour leur relectures attentives, leurs suggestions très pertinentes et leur soutien en général.
Je remercie en particulier Jacques THÉRY (jacques_jean) pour la correction orthographique, Fabien (f-leb) pour la relecture technique et (Winnt) pour ses remarques sur le fond comme sur la forme.