Inspecter les objets Python

Retour à la liste des articles

05 avril 2019

Un des premiers réflexes lorsqu'un programme a un comportement inattendu doit être d'examiner les objets qui le composent, afin de comprendre l'erreur et pouvoir remonter à sa source.

Cet article explique comment utiliser les fonctions natives dir et vars de Python pour examiner les objets d'un programme.

Prérequis pour cet article

Assure-toi d'avoir les connaissances nécessaires pour cet article en terminant la lecture du chapitre 6 « Un programme dynamique avec Python ».

Tu ne possèdes pas encore Génies du code ? Il s'agit d'une méthode illustrée, adaptée à tous les niveaux, qui te fera découvrir la programmation à travers la réalisation de ton propre site web de A à Z. Les deux premiers chapitres sont disponibles gratuitement dans leur intégralité !

Découvrir Génies du code

Affichage des valeurs pendant l'exécution

Dans les cas simples, l'affichage de certaines valeurs juste avant l'erreur peut donner une indication sur son origine. Prenons comme exemple le programme présenté dans le chapitre 6, au point de contrôle numéro 7 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def show_question(question, options):
    print(question)

    for option in options:
        print("- " + option)

    print("Entrez une réponse:")

    answer = input()
    if options[answer]:
        print("Bravo!")
        return True
    else:
        print("La réponse n'est pas correcte")
        return False

score = 0
questions = [
    ["Quelle est la vitesse de pointe d'un thon?", {"20 km/h": False, "40 km/h": False, "80 km/h": True}],
    ["Les poissons ont-ils tous des écailles?", {"Oui": False, "Non": True}]
]

for question in questions:
    is_correct = show_question(question[0], question[1])

    if is_correct:
        score += 1

print("Vous avez répondu correctement à " + str(score) + " questions sur " + str(len(questions)))

Comme on l'a vu, le programme plante lorsque tu entres une réponse qui ne fait pas partie des options à choix :

$ python3 quiz_2.py
Quelle est la vitesse de pointe d’un thon?
- 20 km/h
- 40 km/h
- 80 km/h
Votre réponse: 60 km/h
Traceback (most recent call last):
    File "/home/julie/Bureau/web/quiz_2.py", line 21, in <module>
        correct = show_question(question[0], question[1])
    File "/home/julie/Bureau/web/quiz_2.py", line 7, in show_question
        if options[answer]:
KeyError: '60 km/h'

Pour comprendre l'origine de cette erreur, on aimerait savoir ce que contient la variable options. Pour ce faire, on peut utiliser la fonction print, qui affiche le paramètre qui lui est passé.

Commence par télécharger le code du point de contrôle, enregistre-le (par exemple sur ton bureau), puis extrais l'archive. Ouvre le fichier quiz_2.py dans ton éditeur et ajoute l'instruction print(options) au-dessus de la ligne if options[answer]: pour que le code ressemble à ça :

print(options)
if options[answer]:
    # ...

Ouvre maintenant un terminal, rends-toi dans le dossier où se situe le fichier quiz_2.py (avec la commande cd, dont l'utilisation est expliquée en détail au chapitre 4) puis exécute-le avec la commande python3 quiz_2.py. Tu devrais voir les options s'afficher avant l'erreur :

$ python3 quiz_2.py
...
{'20 km/h': False, '40 km/h': False, '80 km/h': True}
...

De cette façon, tu peux voir que l'option 60 km/h n'est pas présente dans le dictionnaire d'options, et que l'instruction pointée par l'erreur essaie d'accéder à une clef absente du dictionnaire. Dans le cas présent, la valeur de la variable options est assez évidente puisqu'elle se trouve quelques lignes au-dessus dans le code source. Dans les programmes plus complexes, la valeur d'une variable peut changer après un passage dans plusieurs fonctions, et c'est très utile dans ces cas de pouvoir suivre son évolution.

Obtenir des information détaillées sur des objets

La valeur affichée par l'instruction print est la représentation textuelle de l'objet passé en paramètre, autrement dit la valeur retournée par la méthode __str__. Heureusement pour nous, le type dict de Python définit cette méthode, qui donne une représentation compréhensible de l'objet. Mais ce n'est pas le cas pour tous les objets, notamment lorsque tu crées tes propres classes.

Pour voir ce que je veux dire, ouvre le fichier quiz_3.py dans ton éditeur, et ajoute une instruction print(question) dans la boucle  pour qu'elle ressemble à ça :

for question in questions:
    print(question)

    # ...

Exécute maintenant le programme avec la commande python3 quiz_3.py. Tu verras apparaître l'information suivante :

<__main__.Question object at 0x7fdfd0afacab>

Ça nous fait une belle jambe, hein ? Heureusement, on peut examiner l'objet en détail grâce aux fonctions dir et vars. Remplace l'instruction print que tu viens d'ajouter par les instructions suivantes :

for question in questions:
    print(dir(question))
    print(vars(question))

    # ...

Si tu réexécutes ton programme, tu verras les informations suivantes s'afficher :

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
 '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
 '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_correct_answer',
 'is_correct', 'label', 'options', 'points', 'show']

{'label': "Quelle est la vitesse de pointe d'un thon?",
 'options': [<__main__.Option object at 0x7f98d3671c50>, <__main__.Option object at 0x7f98d3671c88>, <__main__.Option object at 0x7f98d3671cc0>],
 'points': 3}

La première information, renvoyée par dir, est la liste des attributs qui existent sur l'objet. Tu y retrouveras aussi bien les méthodes (get_correct_answer, is_correct, show) que les propriétés (label, options, points). Les méthodes qui sont entourées de __ sont des méthodes spéciales de Python. La fonction dir est donc très pratique pour savoir comment utiliser un objet puisqu'elle donne aussi bien la liste des méthodes qu'on peut utiliser sur l'objet que ses propriétés.

La seconde information, renvoyée par vars, est un dictionnaire qui représente les propriétés qui existent sur l'objet. Les clefs de ce dictionnaire représentent les noms des attributs (label, options, points) et les valeurs représentent les valeurs de ces attributs. La fonction vars est donc très pratique pour voir toutes les informations qu'un objet contient ainsi que leurs valeurs.

Note que si tu utilises cette technique pour débugger le code d'un site Django, les valeurs affichées par print apparaîtront dans le terminal dans lequel tu as lancé le serveur Django, et pas sur la page web.

Pour aller plus loin

Tu sais maintenant comment utiliser les fonctions dir et vars pour examiner des objets. Dans certains cas, tu préféreras interrompre le programme une fois la valeur affichée. Pour cela tu peux remonter une exception avec la valeur que tu veux examiner, par exemple en écrivant raise Exception(dir(question)).

Néanmoins, cette façon de faire a ses limitations, parce qu'elle t'oblige à modifier ton code et réexécuter ton programme à chaque fois que tu veux examiner une variable différente. Pour les cas plus complexes, tu devrais plutôt utiliser PDB, le debugger Python, qui te permettra d'exécuter ton programme pas à pas et d'examiner les variables au fur et à mesure.

L'utilisation de PDB est expliquée en détail dans l'article « Débugger un programme Python ».

Tu veux en savoir plus ?

Génies du code est une méthode illustrée, adaptée à tous les niveaux, qui t'initiera à la programmation à travers la réalisation de ton propre site web de A à Z. Les deux premiers chapitres sont disponibles gratuitement dans leur intégralité !

Découvrir Génies du code

Et aussi, fais un tour sur les autres articles, tous plus intéressants les uns que les autres, en toute modestie.