Solutions — série 01-2 (types, nombres, input() et conversions)
Rappel de la règle d'or : on n'ouvre ce fichier qu'après avoir terminé (ou sérieusement séché sur) la série. Compare le raisonnement, pas seulement le code : une solution différente qui marche est souvent valable.
Exercice facile a — prédire les types
Raisonnement — Pour chaque ligne, on se demande : quel uniforme porte cette valeur ? Les guillemets font tout basculer en str ; / fabrique toujours un float ; // entre deux int reste un int ; une comparaison fabrique un bool.
Solution
# ex-01-2-a.py
print(type(7)) # <class 'int'>
print(type(7.0)) # <class 'float'> ← le .0 suffit à changer de type
print(type("7")) # <class 'str'> ← guillemets = texte, point final
print(type(7 / 7)) # <class 'float'> ← / donne TOUJOURS un float (ici 1.0)
print(type(7 // 7)) # <class 'int'> ← // entre deux int reste un int (ici 1)
print(type(7 > 7)) # <class 'bool'> ← une comparaison produit True/False
# (ici False : 7 n'est pas > 7)
Pourquoi ça marche — Le type est déterminé par l'ÉCRITURE de la valeur (7 vs 7.0 vs "7") ou par l'OPÉRATION qui la produit (/ → float, // sur des int → int, comparaison → bool). C'est mécanique : aucune de ces six lignes ne demande de calcul mental compliqué, seulement les règles.
Erreur classique sur cet exercice — Répondre int pour 7 / 7 « parce que ça tombe juste ». Non : la règle de / ne regarde pas le résultat, elle s'applique toujours. Autre classique : répondre int pour "7" parce que ça « ressemble » à un nombre — les guillemets gagnent toujours.
Variante plus difficile — Prédis type(7 // 2.0) et type(True). Puis vérifie print(True + True). (Réponses : float — dès qu'un float entre dans l'opération, le résultat est float, même avec // ; bool ; et True + True affiche 2, car bool se comporte comme un nombre dans les calculs — curiosité honnête de Python, à connaître sans en abuser.)
Exercice facile b — prédire les divisions
Raisonnement — Le tableau de la section 4b, appliqué. Pour chaque ligne : quelle opération, et donc quel type de résultat ? Les pièges sont 10 / 5 (tombe juste mais reste un float) et 10 % 5 (reste nul).
Solution
# ex-01-2-b.py
print(9 / 2) # 4.5
print(9 // 2) # 4 ← quotient entier
print(9 % 2) # 1 ← reste : 9 = 4 * 2 + 1
print(2 ** 4) # 16
print(10 / 5) # 2.0 ← PAS 2 : / donne toujours un float
print(10 % 5) # 0 ← 10 est divisible par 5, reste nul
Pourquoi ça marche — // et % forment un couple : 9 // 2 répond « combien de fois 2 tient-il en entier dans 9 ? » (4 fois) et 9 % 2 répond « que reste-t-il ? » (1). Vérification croisée : 4 * 2 + 1 == 9. Un modulo de 0 signifie « divisible » — c'est le test de parité classique (n % 2).
Erreur classique sur cet exercice — Écrire 2 au lieu de 2.0 pour 10 / 5. Sur papier c'est un demi-point ; dans un vrai programme, c'est un affichage 2.0 heures au lieu de 2 heures, ou pire (⏩ au niveau 3, un float utilisé comme indice de liste plante).
Variante plus difficile — Sans exécuter : 17 // 5, 17 % 5, -17 // 5. (Réponses : 3, 2, et -4 — la division entière arrondit vers le bas, pas vers zéro, comme dit en section 4b. Vérifie ensuite à la machine.)
Exercice facile c — prénom, année, âge
Raisonnement — Deux saisies : le prénom reste du texte (aucune conversion), l'année doit devenir un int pour la soustraction. Le calcul 2026 - birth_year exige deux int ; l'affichage final est une f-string qui mélange sans problème texte et nombre.
Solution
# ex-01-2-c.py
name = input("Ton prénom ? ") # str : on la garde telle quelle
birth_year = int(input("Ton année de naissance ? ")) # conversion OBLIGATOIRE
age = 2026 - birth_year # int - int → int
print(f"Bonjour {name}, tu as environ {age} ans.")
Session :
Ton prénom ? Ada
Ton année de naissance ? 1984
Bonjour Ada, tu as environ 42 ans.
Pourquoi ça marche — input() retourne une str dans les deux cas ; seule celle qui sert à un calcul doit être convertie. int(...) enveloppe directement l'input() : la conversion se fait à la saisie, et tout le reste du programme manipule un vrai nombre.
Erreur classique sur cet exercice — Oublier le int() : 2026 - birth_year plante alors avec TypeError: unsupported operand type(s) for -: 'int' and 'str' — remarque que le message pour - est différent de celui pour + (il n'y a pas de « concaténation » possible avec -, donc Python dit juste « opération non supportée entre ces types »). Deuxième classique : convertir le prénom aussi (int(input("Ton prénom ? "))) par excès de zèle → ValueError dès la saisie.
Variante plus difficile — Affiche aussi l'année de ses 100 ans (birth_year + 100) et le nombre d'années restantes d'ici là, calculé. Puis demande-toi : que se passe-t-il si l'utilisateur tape 1984.0 ? Essaie, lis le traceback, et explique pourquoi int() refuse (revois la section 4e).
Exercice facile d — budget et :.2f
Raisonnement — Une soustraction int - float, un affichage formaté à 2 décimales, et une question de type. Dès qu'un float entre dans un calcul, le résultat est un float : Python « élargit » vers le type le plus riche pour ne rien perdre.
Solution
# ex-01-2-d.py
budget = 100
spent = 37.5
remaining = budget - spent
print(f"Reste : {remaining:.2f} €") # Reste : 62.50 €
print(type(remaining)) # <class 'float'>
# int - float → float : dès qu'un float participe au calcul, le résultat
# est un float. Python ne « redescend » jamais vers int tout seul, même
# si la valeur tombait juste.
Pourquoi ça marche — 100 - 37.5 vaut 62.5, un float. Le format :.2f impose deux chiffres après le point à l'AFFICHAGE : 62.5 devient 62.50 à l'écran, mais la variable contient toujours 62.5. Format d'affichage ≠ valeur stockée.
Erreur classique sur cet exercice — Écrire {remaining.2f} (sans les deux-points) → SyntaxError dans la f-string ; ou round(remaining, 2) en espérant afficher 62.50 — round renvoie le NOMBRE 62.5, et un nombre ne garde pas de zéro final. Pour forcer deux décimales à l'écran, c'est :.2f, pas round().
Variante plus difficile — Remplace spent = 37.5 par spent = 81.30 et affiche le reste SANS :.2f. Tu verras 18.700000000000003 : explique pourquoi (section 4c), puis corrige l'affichage avec les deux remèdes et compare. (Note honnête : toutes les soustractions ne déraillent pas — 100 - 37.55 tombe juste. C'est justement cette imprévisibilité qui impose de formater systématiquement les affichages monétaires.)
Exercice moyen a — les deux bugs de l'âge
Raisonnement — On déroule sans exécuter. Ligne 1 : age reçoit une str (règle absolue d'input()). Ligne 2 : age + 10 mélange str et int → premier bug, un TypeError au moment de l'exécution. Ligne 3 : dans la f-string, {future-age} contient un TIRET — Python lit ça comme la soustraction future - age, et aucune variable future n'existe → deuxième bug, un NameError (si le programme arrivait jusque-là). Le vrai nom de la variable a un underscore : future_age.
Solution
# ex-01-2-e.py — version corrigée
age = int(input("Ton âge ? ")) # bug 1 corrigé : conversion en int
future_age = age + 10
print(f"Dans 10 ans tu auras {future_age} ans.") # bug 2 corrigé : underscore
Session :
Ton âge ? 42
Dans 10 ans tu auras 52 ans.
Pourquoi ça marche — La conversion à la saisie rend age calculable partout ensuite. Et dans une f-string, {...} contient une expression Python évaluée telle quelle : {future_age} va chercher la variable, {future-age} calcule future - age. Le tiret n'est jamais un caractère de nom valide — c'est l'erreur 5 de la leçon 01-1 qui revient par la fenêtre.
Erreur classique sur cet exercice — Corriger la ligne 2 en future_age = age + "10" : plus de TypeError… mais future_age vaut "4210" (concaténation !) et l'affichage ment. Corriger un TypeError en alignant les types du mauvais côté est un piège fréquent : demande-toi toujours si le résultat attendu est un nombre ou un texte. Ici c'est un âge : un nombre.
Variante plus difficile — Garde la version buggée et exécute-la vraiment. Quel bug se déclare EN PREMIER, et pourquoi l'autre reste-t-il invisible ? (Réponse : le TypeError de la ligne 2 — Python exécute de haut en bas et s'arrête à la première exception ; la f-string fautive n'est jamais évaluée. Un programme peut donc contenir plus de bugs que son traceback n'en montre : corriger UNE erreur puis relancer, autant de fois que nécessaire.)
Exercice moyen b — les flottants qui surprennent
Raisonnement — Quatre lignes pour ancrer la section 4c. On applique les règles : addition brute de float → résultat possiblement approximatif ; round(x, 2) → nombre arrondi ; :.1f → affichage formaté. La seule vraie question est de savoir QUELLES additions tombent mal — et on ne peut pas le deviner à l'œil : 1.1 + 2.2 déraille aussi.
Solution
# ex-01-2-f.py
print(0.1 + 0.2) # 0.30000000000000004
print(round(0.1 + 0.2, 2)) # 0.3
print(1.1 + 2.2) # 3.3000000000000003
print(f"{1.1 + 2.2:.1f}") # 3.3
Pourquoi ça marche — 0.1, 0.2, 1.1, 2.2 n'ont pas de représentation binaire exacte : chacun est stocké comme « le double 64 bits le plus proche ». Les erreurs microscopiques s'additionnent et deviennent visibles à l'affichage. round(x, 2) renvoie un nouveau nombre arrondi ; :.1f ne touche pas au nombre mais tronque l'affichage à 1 décimale. Note : round(0.1 + 0.2, 2) s'affiche 0.3 et non 0.30 — c'est un nombre, pas un format ; seul :.2f garantit le nombre de décimales À L'ÉCRAN.
Erreur classique sur cet exercice — Prédire 3.3 pour 1.1 + 2.2 en se disant « le piège c'était seulement 0.1 + 0.2 ». Non : le phénomène est général, 0.1 + 0.2 n'est que l'exemple célèbre. À l'inverse, certains calculs tombent juste (0.25 + 0.5 → 0.75, car 0.25 et 0.5 sont exacts en binaire) : on ne PRÉDIT pas à l'œil, on se protège systématiquement à l'affichage.
Variante plus difficile — Essaie print(0.1 + 0.2 == 0.3) (⏩ == sera officiel au niveau 2). Réponse : False — et c'est exactement pourquoi la règle « ne jamais tester l'égalité exacte de deux float » existe. Cherche aussi print(0.25 + 0.5 == 0.75) : True ! Même règle, résultats différents : c'est bien l'imprévisibilité qui rend le test d'égalité interdit.
Exercice moyen c — minutes → heures + minutes
Raisonnement — Le couple // % de l'exemple 3, plus la chaîne complète input → conversion → calcul → affichage. total // 60 donne les heures complètes, total % 60 les minutes restantes. Tout doit rester en int : aucune division / nulle part.
Solution
# ex-01-2-g.py
total_minutes = int(input("Durée en minutes ? ")) # conversion à la saisie
hours = total_minutes // 60 # heures complètes (division entière)
minutes = total_minutes % 60 # ce qui reste une fois les heures retirées
print(f"{total_minutes} min = {hours} h {minutes} min")
Session :
Durée en minutes ? 200
200 min = 3 h 20 min
Pourquoi ça marche — 200 // 60 → 3 (60 tient 3 fois dans 200) et 200 % 60 → 20 (200 − 3×60). Les deux opérandes sont des int, donc les résultats sont des int : l'affichage montre 3, pas 3.0. Vérification mentale systématique : hours * 60 + minutes doit redonner total_minutes.
Erreur classique sur cet exercice — Calculer hours = total_minutes / 60 puis « nettoyer » avec int(hours). Ça marche, mais c'est un détour : on fabrique un float approximatif pour le retronquer ensuite. Pire : round(total_minutes / 60) donne 3 pour 200 mais 4 pour 220 (arrondi, pas quotient !) — et l'affichage 220 min = 4 h -20 min est absurde. Le bon outil est // directement.
Variante plus difficile — Étends à 7384 secondes → 2 h 3 min 4 s. (Deux étages : seconds % 60 pour les secondes, seconds // 60 pour un total de minutes, puis le même couple sur les minutes. C'est le même motif, appliqué deux fois — si tu l'écris sans regarder la solution, la notion est acquise.)
Exercice difficile a — somme des chiffres avec // et %
Raisonnement — L'idée clé (indices 1 et 2) : sur un entier, % 10 donne le dernier chiffre et // 10 retire ce dernier chiffre. En appliquant la paire trois fois, on épluche le nombre de droite à gauche : 472 → 2, puis 47 → 7, puis 4 → 4. Sans boucles (⏩ niveau 2), on écrit les trois extractions à la main.
Solution
# ex-01-2-h.py
n = int(input("Nombre à trois chiffres ? ")) # ex : 472
units = n % 10 # 472 % 10 → 2 : le dernier chiffre
n = n // 10 # 472 // 10 → 47 : on retire le dernier chiffre
tens = n % 10 # 47 % 10 → 7
n = n // 10 # 47 // 10 → 4
hundreds = n # il ne reste que le premier chiffre
total = hundreds + tens + units
print(f"{hundreds} + {tens} + {units} = {total}")
Session :
Nombre à trois chiffres ? 472
4 + 7 + 2 = 13
Pourquoi ça marche — En base 10, 472 = 4×100 + 7×10 + 2. Le reste de la division par 10 est donc toujours le chiffre des unités, et le quotient est « le nombre sans son dernier chiffre ». Réassigner n = n // 10 (droite évaluée d'abord — leçon 01-1) fait avancer l'épluchage : chaque tour de la paire %/// consomme un chiffre. C'est un motif fondamental que tu recroiseras (conversion de bases, checksums, ⏩ boucles du niveau 2 pour un nombre de chiffres quelconque).
Erreur classique sur cet exercice — Extraire les trois chiffres SANS réassigner n :
units = n % 10 # 2 ✅
tens = n % 100 # 72 ❌ c'est les DEUX derniers chiffres, pas le chiffre des dizaines
(Il faudrait n % 100 // 10 — correct mais plus dur à relire.) Autre classique : utiliser / 10 au lieu de // 10 : n devient un float (47.2) et tout l'épluchage déraille.
Variante plus difficile — (1) Même exercice avec un nombre à QUATRE chiffres — compte les lignes ajoutées : tu ressens physiquement pourquoi les boucles du niveau 2 existent. (2) Affiche aussi le nombre renversé (472 → 274), construit uniquement avec *, +, et tes trois variables chiffres. (Piste : units * 100 + tens * 10 + hundreds.)
Mini-projet — « convertisseur de devises »
Raisonnement — L'architecture d'un petit programme interactif, toujours la même : (1) constantes en haut, (2) saisie + conversion, (3) calcul, (4) affichage soigné. Le taux vit dans UNE variable en tête de fichier (réflexe DRY du mini-projet 01-1) ; le montant est un float car l'utilisateur a le droit de taper 99.50 ; tout l'affichage monétaire passe par :.2f.
Solution
# converter.py
# --- constants ------------------------------------------------------------
rate = 1.08 # 1 EUR = 1.08 USD — seule ligne à changer si le taux bouge
# --- banner ---------------------------------------------------------------
print("=== Convertisseur EUR -> USD ===")
print(f"Taux : 1 EUR = {rate} USD")
# --- input ----------------------------------------------------------------
amount_eur = float(input("Montant en euros ? ")) # float : accepte 99.50
# --- computation ----------------------------------------------------------
amount_usd = amount_eur * rate
# --- display --------------------------------------------------------------
print(f"{amount_eur:.2f} EUR = {amount_usd:.2f} USD")
print(f"Soit environ {round(amount_usd)} dollars tout rond.")
Session :
=== Convertisseur EUR -> USD ===
Taux : 1 EUR = 1.08 USD
Montant en euros ? 250
250.00 EUR = 270.00 USD
Soit environ 270 dollars tout rond.
Extension — l'aller-retour :
back_to_eur = amount_usd / rate
print(f"Contre-vérification : {amount_usd:.2f} USD = {back_to_eur:.2f} EUR")
print(f"(valeur brute : {back_to_eur})") # parfois 249.99999999999997 !
Pourquoi ça marche — float(input(...)) accepte 250 comme 99.50 (alors que int(input(...)) planterait sur 99.50 — c'est pour ça que le montant est un float). Le calcul est une multiplication de float, donc le résultat peut être approximatif : :.2f garantit un affichage monétaire propre quoi qu'il arrive. round(amount_usd) sans deuxième argument arrondit à l'entier — un int, parfait pour « tout rond ». Et l'aller-retour de l'extension rend visible l'approximation des float : diviser puis multiplier par 1.08 ne redonne pas toujours exactement le nombre de départ.
Erreur classique sur cet exercice — int(input("Montant en euros ? ")) : le programme marche avec 250 et plante avec 99.50 (ValueError: invalid literal for int() with base 10: '99.50'). Tester son programme avec UNE seule valeur « facile » ne prouve rien : teste toujours au moins une valeur à virgule, une petite, une grande. Deuxième classique : taper 1.08 en dur dans le calcul ET dans la bannière — le jour où le taux change, l'un des deux ment.
Variante plus difficile — (1) Ajoute une deuxième devise (GBP à 0.84) : le même montant affiché en USD et en GBP, chaque taux dans sa variable. (2) Demande AUSSI le taux à l'utilisateur (float(input(...))) au lieu de le figer — le programme devient un convertisseur universel. (3) ⏩ Au niveau 2, tu ajouteras un if pour refuser un montant négatif ; au niveau 5, un try/except pour survivre à abc. Ce petit fichier va grandir avec toi — garde-le.