04 — Calculatrice
Niveau requis : 2 pour la v1, 4 pour la v2 / Difficulté : 2/5 (v1), 3/5 (v2) / Durée estimée : 1 h (v1) + 1–2 h (v2)
Objectif
Une calculatrice de terminal : l'utilisateur entre un premier nombre, un opérateur (+, -, *, /), un second nombre, et obtient le résultat. Le programme enchaîne les calculs jusqu'à ce qu'on tape q.
Ce projet se fait deux fois :
- v1 (niveau 2) — tout dans le flux principal, avec
if/elif. - v2 (niveau 4) — le MÊME comportement, refactoré en fonctions. C'est le cœur pédagogique du projet : tu verras concrètement ce que les fonctions apportent à un code que tu connais déjà par cœur.
Notions utilisées
input(),float(), f-strings — niveau 1if/elif/else,while— niveau 2- Division par zéro : la détecter AVANT de calculer — niveau 2
- v2 : définition de fonctions, paramètres,
return— niveau 4 - v2 : découpage d'un programme en responsabilités — niveau 4
Cahier des charges
Commun aux deux versions :
- Fichier
calculator.py(v1), puiscalculator_v2.py(v2) — on garde la v1 pour comparer. - Opérateurs supportés :
+,-,*,/. - Le résultat s'affiche sous la forme
12.0 + 3.0 = 15.0. - Division par zéro : message « Division par zéro impossible. », pas de traceback, et le programme continue.
- Opérateur inconnu : message clair, le programme continue.
- Taper
qà la place du premier nombre quitte proprement.
Spécifique v2 :
- Une fonction par responsabilité, au minimum :
compute(a, operator, b)qui retourne le résultat (elle n'affiche RIEN), etmain()qui gère la boucle et les entrées/sorties. - Comportement extérieur identique à la v1 : mêmes messages, mêmes cas. Test : dérouler le même scénario sur les deux versions donne la même sortie.
Étapes de construction
v1 — niveau 2
- Un seul calcul, un seul opérateur : demande deux nombres, affiche leur somme. Lance et vérifie.
- Demande aussi l'opérateur et branche
+,-,*,/avecif/elif.else= « opérateur inconnu ». Teste les cinq cas. - Protège la division : si l'opérateur est
/et le second nombre 0, message dédié, pas de calcul. Teste5 / 0. - Enveloppe le tout dans un
while Trueavec sortie surq(tapé comme premier « nombre » — teste l'ordre de tes vérifications :qdoit être détecté AVANTfloat()). - Passe en revue les messages : cohérents, sans faute, avec les valeurs rappelées. C'est la v1 finie — commit.
v2 — niveau 4
- Copie
calculator.pyencalculator_v2.py. Extrais d'abordcompute(a, operator, b): elle retourne le résultat ouNonesi opérateur inconnu / division par zéro. Le flux principal l'appelle. Vérifie que tout marche encore. - Extrais
read_number(prompt)qui gère la saisie (et le casq— décide de ce qu'elle retourne dans ce cas, et assume ce choix). Puis mets la boucle dansmain(), appelée parif __name__ == "__main__":(⏩ expliqué en détail au niveau 5 — pour l'instant : « c'est ce qui lancemain()quand on exécute le fichier »). - Déroule le même scénario de test sur v1 et v2 (mêmes entrées) et compare les sorties ligne à ligne. Identiques = refactoring réussi. Commit.
Extensions possibles
- Ajouter
//,%et**(v1 ou v2). - v2 : accepter une expression sur une ligne (
12 + 3) et la découper avecstr.split()— attention aux entrées mal formées. - v2 : garder l'historique des calculs en liste et l'afficher sur commande
h(niveau 3 requis). - v2 : écrire quelques tests de
compute()— un avant-goût du niveau 8 (assert compute(2, "+", 3) == 5).
Erreurs fréquentes sur ce projet
ZeroDivisionError: division by zero: la protection doit intervenir AVANT le calcul. La versiontry/exceptpropre viendra au niveau 5 ; ici, on teste explicitement.- Tester
qAPRÈSfloat():float("q")lèveValueErroravant que ton test s'exécute. L'ordre des vérifications est une décision de conception, pas un détail. - v2 :
compute()qui fait desprint()au lieu dereturn— la fonction devient inutilisable ailleurs (et l'extension 4 devient impossible). Une fonction de calcul calcule, elle ne parle pas. - v2 : oublier de récupérer le retour (
compute(a, op, b)seul sur sa ligne) : le résultat est calculé puis perdu. Symptôme : plus rien ne s'affiche. - Comparer des floats à
0avec des valeurs saisies : icib == 0suffit, mais retiens que l'égalité stricte entre floats calculés est piégeuse (⏩ approfondi au niveau 8).
Critères de réussite
- v1 : les 4 opérateurs, la division par zéro et l'opérateur inconnu se comportent comme spécifié.
- v1 :
qquitte proprement, à tout moment. - v2 : sortie strictement identique à la v1 sur un même scénario.
- v2 :
compute()ne contient aucunprint()niinput(). - v2 : je peux expliquer ce que chaque fonction reçoit et retourne, sans regarder le code.
Commit conseillé
Deux commits, un par version :
git add projects/mini-projects/04-calculatrice/
git commit -m "project: add calculator"
# ... puis, apres la v2 :
git commit -m "project: add calculator refactored with functions"
Puis entrée de journal + git push.