alpinux-wiki/docs/presentations/Python/02-conditions.md
2026-10-15 00:28:56 +02:00

1680 lines
44 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Chapitre 02 — Les conditions : faire des choix
> **⏱️ Durée estimée :** 4 heures
>
> **📋 Prérequis :** [Chapitre 01 — Variables, types et opérateurs](01-variables-types-operateurs.md) : variables, types `int`/`float`/`str`/`bool`, `input()` avec conversion, f-strings.
>
> **🎯 Objectifs — À la fin de ce chapitre, tu sauras :**
> - comparer des valeurs avec `==`, `!=`, `<`, `>`, `<=`, `>=` et obtenir un booléen ;
> - faire exécuter du code seulement si une condition est vraie, avec `if`, `elif` et `else` ;
> - indenter correctement ton code pour délimiter des blocs ;
> - combiner plusieurs conditions avec `and`, `or` et `not` ;
> - utiliser l'expression conditionnelle sur une ligne (`a if condition else b`) ;
> - choisir parmi plusieurs cas de façon élégante avec `match` / `case`.
---
## Cours
### 1. Pourquoi des conditions ?
Jusqu'ici, tes programmes exécutaient toutes leurs lignes, de haut en bas, quoi qu'il arrive. Mais la vie est faite de choix : **s'il** pleut, je prends un parapluie, **sinon** je prends mes lunettes de soleil. Un distributeur de billets vérifie **si** ton code est correct. Un jeu vérifie **si** tu as perdu toutes tes vies.
Les **conditions** permettent à un programme de prendre des décisions : exécuter certaines lignes seulement dans certaines situations. Pour cela, il faut d'abord savoir poser des questions dont la réponse est **oui** ou **non**, c'est-à-dire `True` ou `False`.
### 2. Les opérateurs de comparaison
Un opérateur de comparaison compare deux valeurs et renvoie un **booléen** (`True` ou `False`).
| Opérateur | Signification | Exemple | Résultat |
|---|---|---|---|
| `==` | est égal à | `5 == 5` | `True` |
| `!=` | est différent de | `5 != 3` | `True` |
| `<` | est strictement inférieur à | `3 < 5` | `True` |
| `>` | est strictement supérieur à | `3 > 5` | `False` |
| `<=` | est inférieur ou égal à | `5 <= 5` | `True` |
| `>=` | est supérieur ou égal à | `4 >= 5` | `False` |
```python
age = 17
print(age >= 18)
print(age == 17)
print(age != 17)
majeur = age >= 18 # on peut stocker le résultat dans une variable
print(majeur, type(majeur))
```
Sortie :
```text
False
True
False
False <class 'bool'>
```
> ⚠️ **`=` n'est pas `==` !** Un seul `=` **affecte** une valeur (« colle l'étiquette »). Deux `==` **comparent** (« est-ce que c'est égal ? »). C'est l'erreur la plus fréquente de ce chapitre.
#### 2.1. Comparer des chaînes
On peut comparer des chaînes. L'égalité est **stricte** : majuscules, espaces et accents comptent.
```python
print("chat" == "chat")
print("chat" == "Chat")
print("chat" == "chat ")
print("été" == "ete")
```
Sortie :
```text
True
False
False
False
```
On peut aussi comparer des chaînes avec `<` et `>` : c'est l'ordre « alphabétique », mais attention, il suit en réalité les codes des caractères. Toutes les majuscules passent avant toutes les minuscules, et les chiffres sont comparés **caractère par caractère** :
```python
print("abricot" < "banane")
print("Zoo" < "abc")
print("10" < "9")
```
Sortie :
```text
True
True
True
```
`"10" < "9"` est vrai, car on compare d'abord `"1"` et `"9"`, et `"1"` vient avant. Encore une bonne raison de convertir les saisies en nombres avec `int()` !
#### 2.2. Comparer des nombres à virgule
À cause de l'imprécision des floats vue au chapitre 01, l'égalité exacte entre floats est piégeuse :
```python
print(0.1 + 0.2 == 0.3)
print(abs((0.1 + 0.2) - 0.3) < 0.000001)
```
Sortie :
```text
False
True
```
Pour comparer deux floats, on vérifie plutôt que leur écart est minuscule. (Le module `math` propose `math.isclose()` pour ça, voir le [chapitre 09](09-modules-bibliotheque-standard.md).)
#### 2.3. Comparaisons enchaînées
Python permet d'écrire des encadrements comme en maths :
```python
note = 14
print(10 <= note <= 20) # note est-elle entre 10 et 20 ?
print(0 < note < 10)
```
Sortie :
```text
True
False
```
`10 <= note <= 20` signifie « `10 <= note` **et** `note <= 20` ». C'est très lisible, profites-en.
### 3. L'instruction `if`
Voici la structure de base :
```python
temperature = 31
if temperature > 30:
print("Il fait très chaud !")
print("Pense à boire de l'eau.")
print("Bonne journée.")
```
Sortie :
```text
Il fait très chaud !
Pense à boire de l'eau.
Bonne journée.
```
Décortiquons :
1. Le mot-clé **`if`** (« si »), suivi d'une **condition** (`temperature > 30`), suivie de **deux-points** `:`.
2. En dessous, un **bloc** de lignes **indentées** (décalées vers la droite de 4 espaces). Ces lignes ne s'exécutent **que si** la condition vaut `True`.
3. La première ligne qui n'est plus indentée (`print("Bonne journée.")`) marque la fin du bloc : elle s'exécute dans tous les cas.
Avec `temperature = 20`, la condition est fausse : le bloc est sauté.
```python
temperature = 20
if temperature > 30:
print("Il fait très chaud !")
print("Pense à boire de l'eau.")
print("Bonne journée.")
```
Sortie :
```text
Bonne journée.
```
#### 3.1. L'indentation : la grammaire de Python
Dans beaucoup de langages, on délimite les blocs avec des accolades `{ }`. Python, lui, utilise **l'indentation** : le décalage en début de ligne. Ce n'est pas une simple question d'esthétique : c'est **obligatoire**, et c'est ce qui dit à Python quelles lignes font partie du bloc.
Pense à une recette : « Si tu as des invités : mets la table, sors les verres. » Les actions « mets la table » et « sors les verres » sont en retrait, car elles dépendent du « si ».
Règles d'or :
- la convention est d'indenter avec **4 espaces** (VS Code le fait automatiquement quand tu appuies sur Tab ou après les `:`) ;
- toutes les lignes d'un même bloc ont **exactement** la même indentation ;
- ne mélange jamais espaces et tabulations.
Compare ces deux programmes, qui ne diffèrent que par l'indentation de la dernière ligne :
```python
solde = 50
if solde < 0:
print("Attention : compte à découvert !")
print("Contacte ta banque.")
```
Ce premier programme n'affiche **rien** : la condition est fausse, donc les deux `print` sont sautés.
```python
solde = 50
if solde < 0:
print("Attention : compte à découvert !")
print("Contacte ta banque.")
```
Sortie :
```text
Contacte ta banque.
```
Dans le second programme, le dernier `print` ne fait plus partie du bloc : il s'exécute toujours.
#### 3.2. Un bloc vide : `pass`
Un bloc ne peut pas être vide. Si tu veux prévoir un `if` sans encore savoir quoi mettre dedans, écris `pass` (« ne rien faire ») :
```python
niveau = 3
if niveau > 10:
pass # TODO : gérer les niveaux bonus plus tard
print("Partie lancée")
```
Sortie :
```text
Partie lancée
```
### 4. `else` : sinon…
`else` (« sinon ») introduit le bloc à exécuter quand la condition est **fausse**. Exactement l'un des deux blocs est exécuté, jamais les deux, jamais aucun.
```python
age = int(input("Quel âge as-tu ? "))
if age >= 18:
print("Tu es majeur.")
else:
print("Tu es mineur.")
print(f"Encore {18 - age} an(s) à attendre.")
```
Exemple d'exécution :
```text
Quel âge as-tu ? 15
Tu es mineur.
Encore 3 an(s) à attendre.
```
Le `else` est aligné avec le `if`, se termine par `:`, et n'a **pas** de condition.
Un grand classique : tester si un nombre est pair, avec le modulo vu au chapitre 01.
```python
nombre = int(input("Un nombre entier : "))
if nombre % 2 == 0:
print(f"{nombre} est pair.")
else:
print(f"{nombre} est impair.")
```
Exemple d'exécution :
```text
Un nombre entier : 7
7 est impair.
```
### 5. `elif` : sinon, si…
Quand il y a plus de deux possibilités, on enchaîne des `elif` (contraction de *else if*, « sinon, si »).
```python
note = float(input("Ta note sur 20 : "))
if note >= 16:
mention = "Très bien"
elif note >= 14:
mention = "Bien"
elif note >= 12:
mention = "Assez bien"
elif note >= 10:
mention = "Passable"
else:
mention = "Non admis"
print(f"Mention : {mention}")
```
Exemple d'exécution :
```text
Ta note sur 20 : 14.5
Mention : Bien
```
**Comment Python lit ce code :** il teste les conditions **dans l'ordre, de haut en bas**, et exécute **uniquement le premier bloc** dont la condition est vraie. Ensuite, il saute directement après toute la structure, sans regarder les autres.
Avec `14.5` : `14.5 >= 16` est faux → on passe ; `14.5 >= 14` est vrai → mention « Bien », et c'est fini. Peu importe que `14.5 >= 12` soit aussi vrai : on ne le teste même pas.
C'est pour cela que **l'ordre des conditions est crucial**. Regarde ce qui se passe si on les inverse :
```python
note = 18
if note >= 10:
mention = "Passable"
elif note >= 16:
mention = "Très bien"
else:
mention = "Non admis"
print(mention)
```
Sortie :
```text
Passable
```
Un 18 qui obtient « Passable » ! La première condition vraie gagne, et `18 >= 10` est vrai. Quand les conditions se chevauchent, va **du plus restrictif au moins restrictif**.
**Résumé de la structure complète :**
```text
if condition_1: ← obligatoire, toujours en premier
...
elif condition_2: ← facultatif, autant qu'on veut
...
elif condition_3:
...
else: ← facultatif, toujours en dernier, sans condition
...
```
> 💡 **`if` successifs ou `elif` ?** Plusieurs `if` à la suite sont **indépendants** : chacun est testé. Une chaîne `if`/`elif` est **exclusive** : un seul bloc s'exécute.
```python
nombre = 15
print("Avec des if séparés :")
if nombre % 3 == 0:
print(" divisible par 3")
if nombre % 5 == 0:
print(" divisible par 5")
print("Avec elif :")
if nombre % 3 == 0:
print(" divisible par 3")
elif nombre % 5 == 0:
print(" divisible par 5")
```
Sortie :
```text
Avec des if séparés :
divisible par 3
divisible par 5
Avec elif :
divisible par 3
```
### 6. Les conditions imbriquées
Un bloc peut contenir lui-même un `if`. On parle de conditions **imbriquées**. Chaque niveau ajoute 4 espaces d'indentation.
```python
a_un_billet = True
age = 10
if a_un_billet:
print("Bienvenue au parc !")
if age < 12:
print("Voici ton bracelet enfant.")
else:
print("Voici ton bracelet adulte.")
else:
print("Merci de passer par la billetterie.")
```
Sortie :
```text
Bienvenue au parc !
Voici ton bracelet enfant.
```
Remarque `if a_un_billet:` : comme `a_un_billet` est déjà un booléen, inutile d'écrire `if a_un_billet == True:`. C'est plus court et plus naturel (« si on a un billet »).
> 💡 Au-delà de deux ou trois niveaux d'imbrication, le code devient difficile à lire. Les opérateurs logiques de la section suivante permettent souvent d'« aplatir » les conditions.
### 7. Les opérateurs logiques `and`, `or`, `not`
Ils permettent de combiner plusieurs conditions.
#### 7.1. `and` : les deux doivent être vraies
```python
age = 25
a_le_permis = True
if age >= 18 and a_le_permis:
print("Tu peux louer une voiture.")
```
Sortie :
```text
Tu peux louer une voiture.
```
#### 7.2. `or` : au moins une doit être vraie
```python
jour = "dimanche"
if jour == "samedi" or jour == "dimanche":
print("C'est le week-end !")
```
Sortie :
```text
C'est le week-end !
```
#### 7.3. `not` : inverse une condition
```python
est_connecte = False
if not est_connecte:
print("Merci de te connecter.")
```
Sortie :
```text
Merci de te connecter.
```
#### 7.4. Tables de vérité
| `a` | `b` | `a and b` | `a or b` | `not a` |
|---|---|---|---|---|
| `True` | `True` | `True` | `True` | `False` |
| `True` | `False` | `False` | `True` | `False` |
| `False` | `True` | `False` | `True` | `True` |
| `False` | `False` | `False` | `False` | `True` |
Moyen mnémotechnique : `and` est **exigeant** (tout doit être vrai), `or` est **arrangeant** (une seule suffit).
#### 7.5. Priorités et parenthèses
L'ordre de priorité est : d'abord les comparaisons, puis `not`, puis `and`, puis `or`. Comme pour `*` et `+`, `and` passe avant `or`. Dans le doute, **mets des parenthèses** :
```python
age = 15
accompagne = False
est_vip = True
# Entrée autorisée si : (majeur OU accompagné) ET VIP
if (age >= 18 or accompagne) and est_vip:
print("Entrée autorisée (version avec parenthèses)")
else:
print("Entrée refusée (version avec parenthèses)")
# Sans parenthèses, Python lit : age >= 18 or (accompagne and est_vip)
if age >= 18 or accompagne and est_vip:
print("Entrée autorisée (version sans parenthèses)")
else:
print("Entrée refusée (version sans parenthèses)")
```
Sortie :
```text
Entrée refusée (version avec parenthèses)
Entrée refusée (version sans parenthèses)
```
Ici les deux résultats coïncident par hasard, mais changeons une valeur :
```python
age = 20
accompagne = False
est_vip = False
if (age >= 18 or accompagne) and est_vip:
print("Avec parenthèses : autorisé")
else:
print("Avec parenthèses : refusé")
if age >= 18 or accompagne and est_vip:
print("Sans parenthèses : autorisé")
else:
print("Sans parenthèses : refusé")
```
Sortie :
```text
Avec parenthèses : refusé
Sans parenthèses : autorisé
```
Les parenthèses changent le sens. Écris toujours ce que tu veux dire explicitement.
#### 7.6. L'évaluation paresseuse
Python est « paresseux » : avec `and`, si la première condition est fausse, il ne regarde même pas la seconde (le résultat sera faux de toute façon). Avec `or`, si la première est vraie, il s'arrête aussi. C'est utile pour se protéger d'une erreur :
```python
nb_personnes = 0
total = 120
if nb_personnes != 0 and total / nb_personnes > 50:
print("Repas cher !")
else:
print("Pas de calcul possible ou repas raisonnable.")
```
Sortie :
```text
Pas de calcul possible ou repas raisonnable.
```
Comme `nb_personnes != 0` est faux, la division par zéro n'est jamais tentée.
### 8. La « vérité » des valeurs
Une condition n'a pas forcément besoin d'être un booléen. Python sait évaluer n'importe quelle valeur comme vraie ou fausse, selon les règles de `bool()` vues au chapitre 01 :
- **considérés comme faux** : `False`, `0`, `0.0`, la chaîne vide `""` (et `None`, ainsi que les collections vides, que tu verras plus tard) ;
- **considéré comme vrai** : tout le reste.
```python
pseudo = input("Ton pseudo (laisse vide pour rester anonyme) : ")
if pseudo:
print(f"Bienvenue, {pseudo} !")
else:
print("Bienvenue, visiteur anonyme !")
```
Exemple d'exécution (l'utilisateur appuie directement sur Entrée) :
```text
Ton pseudo (laisse vide pour rester anonyme) :
Bienvenue, visiteur anonyme !
```
`if pseudo:` se lit « si le pseudo n'est pas vide ». C'est une écriture très courante en Python.
### 9. L'opérateur `in` avec les chaînes
`in` teste si un morceau de texte est **contenu** dans une chaîne. C'est une façon très pratique d'éviter les longues séries de `or` :
```python
email = "camille@exemple.fr"
if "@" in email:
print("L'adresse contient bien un @.")
lettre = "e"
if lettre in "aeiouy":
print(f"'{lettre}' est une voyelle.")
if "z" not in email:
print("Pas de z dans l'adresse.")
```
Sortie :
```text
L'adresse contient bien un @.
'e' est une voyelle.
Pas de z dans l'adresse.
```
`in` fonctionne aussi avec les listes, les tuples et les dictionnaires, que tu découvriras au [chapitre 04](04-structures-de-donnees.md).
### 10. Rendre les saisies plus tolérantes
Si tu demandes « oui ou non ? », l'utilisateur peut taper `oui`, `Oui`, `OUI` ou ` oui `… et `"Oui" == "oui"` est faux. Deux outils des chaînes (détaillés au [chapitre 06](06-chaines-et-comprehensions.md)) règlent le problème :
- `texte.lower()` renvoie le texte en minuscules ;
- `texte.strip()` renvoie le texte sans les espaces au début et à la fin.
On peut les enchaîner :
```python
reponse = input("Veux-tu continuer (oui/non) ? ").strip().lower()
if reponse == "oui":
print("C'est parti !")
elif reponse == "non":
print("À bientôt.")
else:
print("Je n'ai pas compris.")
```
Exemple d'exécution :
```text
Veux-tu continuer (oui/non) ? OUI
C'est parti !
```
### 11. L'expression conditionnelle (le « if sur une ligne »)
Quand on veut simplement choisir entre **deux valeurs**, il existe une forme compacte :
```text
valeur_si_vrai if condition else valeur_si_faux
```
```python
age = 16
statut = "majeur" if age >= 18 else "mineur"
print(statut)
nb_messages = 1
print(f"Tu as {nb_messages} message{'s' if nb_messages > 1 else ''}.")
nb_messages = 3
print(f"Tu as {nb_messages} message{'s' if nb_messages > 1 else ''}.")
```
Sortie :
```text
mineur
Tu as 1 message.
Tu as 3 messages.
```
C'est l'équivalent de :
```python
age = 16
if age >= 18:
statut = "majeur"
else:
statut = "mineur"
print(statut)
```
Sortie :
```text
mineur
```
> 💡 Réserve cette forme aux cas **simples**. Dès qu'il y a plus de deux possibilités ou plusieurs instructions, un `if`/`else` classique est plus lisible.
### 12. `match` / `case` : choisir parmi plusieurs cas
Quand tu compares **une même valeur** à une série de possibilités, une longue chaîne de `elif` devient vite lourde :
```python
commande = "aide"
if commande == "jouer":
print("Lancement du jeu...")
elif commande == "aide":
print("Voici l'aide.")
elif commande == "quitter":
print("Au revoir !")
else:
print("Commande inconnue.")
```
Sortie :
```text
Voici l'aide.
```
Depuis Python 3.10, l'instruction **`match`** permet d'écrire cela plus clairement :
```python
commande = "aide"
match commande:
case "jouer":
print("Lancement du jeu...")
case "aide":
print("Voici l'aide.")
case "quitter":
print("Au revoir !")
case _:
print("Commande inconnue.")
```
Sortie :
```text
Voici l'aide.
```
Comment ça marche :
- `match valeur:` indique la valeur à examiner ;
- chaque `case motif:` propose une possibilité. Python les teste **dans l'ordre** et exécute le **premier** qui correspond ;
- `case _:` (le tiret bas) est le cas « joker » : il correspond à **tout**. C'est l'équivalent du `else`. Mets-le toujours en dernier.
Pense à un aiguilleur de gare : le train (la valeur) arrive, et l'aiguilleur l'envoie sur la première voie (le `case`) qui lui correspond.
#### 12.1. Plusieurs valeurs pour un même cas : `|`
Le symbole `|` signifie « ou » dans un `case` :
```python
jour = "samedi"
match jour:
case "samedi" | "dimanche":
print("Week-end : grasse matinée !")
case "lundi" | "mardi" | "mercredi" | "jeudi" | "vendredi":
print("Semaine : debout !")
case _:
print("Ce n'est pas un jour valide.")
```
Sortie :
```text
Week-end : grasse matinée !
```
#### 12.2. Ajouter une condition : la garde `if`
On peut capturer la valeur dans un nom, puis ajouter une condition avec `if` après le motif. On appelle ça une **garde** :
```python
temperature = 23
match temperature:
case 0:
print("Pile le point de congélation.")
case t if t < 0:
print(f"{t} °C : il gèle !")
case t if t < 25:
print(f"{t} °C : température agréable.")
case t:
print(f"{t} °C : il fait chaud !")
```
Sortie :
```text
23 °C : température agréable.
```
Ici, `case t if t < 0:` signifie : « appelle la valeur `t`, et ce cas s'applique si `t < 0` ». Le dernier `case t:` sans garde attrape tout ce qui reste.
> 💡 **`match` ou `if`/`elif` ?** Utilise `match` quand tu compares **une seule valeur** à des valeurs précises (commandes, choix de menu, codes). Pour des intervalles et des conditions complexes, `if`/`elif` reste souvent plus naturel. `match` est en réalité bien plus puissant (il sait décomposer des structures de données), tu en verras d'autres usages plus tard.
### 13. Exemple complet : la calculatrice
Récapitulons avec une petite calculatrice qui utilise `match`, des conditions imbriquées et une vérification de la division par zéro.
```python
# Calculatrice à 4 opérations
a = float(input("Premier nombre : "))
operateur = input("Opérateur (+, -, *, /) : ").strip()
b = float(input("Second nombre : "))
match operateur:
case "+":
print(f"{a} + {b} = {a + b}")
case "-":
print(f"{a} - {b} = {a - b}")
case "*" | "x":
print(f"{a} × {b} = {a * b}")
case "/":
if b == 0:
print("Erreur : division par zéro impossible.")
else:
print(f"{a} / {b} = {a / b:.4g}")
case _:
print(f"Opérateur « {operateur} » inconnu.")
```
Exemple d'exécution :
```text
Premier nombre : 10
Opérateur (+, -, *, /) : /
Second nombre : 3
10.0 / 3.0 = 3.333
```
Le format `:.4g` affiche au plus 4 chiffres significatifs, en supprimant les zéros inutiles.
---
## Pièges fréquents
### Piège 1 — Oublier les deux-points
```python
age = 20
if age >= 18
print("Majeur")
```
```text
File "conditions.py", line 2
if age >= 18
^
SyntaxError: expected ':'
```
**Correction :** chaque ligne `if`, `elif`, `else`, `match` et `case` se termine par `:`.
### Piège 2 — Oublier d'indenter
```python
age = 20
if age >= 18:
print("Majeur")
```
```text
File "conditions.py", line 3
print("Majeur")
^^^^^
IndentationError: expected an indented block after 'if' statement on line 2
```
**Correction :** décale la ligne de 4 espaces : ` print("Majeur")`.
### Piège 3 — Une indentation incohérente
```python
age = 20
if age >= 18:
print("Majeur")
print("fin")
```
```text
File "conditions.py", line 4
print("fin")
^
IndentationError: unindent does not match any outer indentation level
```
La dernière ligne a 2 espaces : elle n'est alignée ni avec le bloc (4 espaces) ni avec le `if` (0 espace).
**Correction :** chaque ligne doit être alignée exactement sur un niveau existant. Dans VS Code, le nombre d'espaces de la ligne courante est indiqué en bas à droite, et le menu *Affichage* → *Afficher les espaces* (*Render Whitespace*) les rend visibles.
### Piège 4 — `=` au lieu de `==`
```python
age = 20
if age = 18:
print("Pile 18 ans")
```
```text
File "conditions.py", line 2
if age = 18:
^^^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
```
**Correction :** `if age == 18:`. Python te le suggère d'ailleurs dans le message.
### Piège 5 — Comparer une saisie non convertie
```python
age = input("Âge : ")
if age >= 18:
print("Majeur")
```
```text
Âge : 20
Traceback (most recent call last):
File "conditions.py", line 2, in <module>
if age >= 18:
^^^^^^^^^
TypeError: '>=' not supported between instances of 'str' and 'int'
```
Python refuse de comparer le texte `"20"` au nombre `18`. Et avec `==`, c'est encore plus sournois : `"18" == 18` ne provoque pas d'erreur, mais vaut toujours `False` !
**Correction :** `age = int(input("Âge : "))`.
### Piège 6 — Le faux ami `or`
```python
couleur = "vert"
if couleur == "rouge" or "bleu":
print(f"{couleur} est une couleur primaire")
```
Sortie :
```text
vert est une couleur primaire
```
Aucune erreur… mais un résultat faux ! Python lit `(couleur == "rouge") or ("bleu")`. Or `"bleu"` est une chaîne non vide, donc considérée comme **vraie** : la condition est toujours vraie.
**Correction :** répète la comparaison, ou utilise `in` :
```python
couleur = "vert"
if couleur == "rouge" or couleur == "bleu" or couleur == "jaune":
print(f"{couleur} est une couleur primaire")
else:
print(f"{couleur} n'est pas une couleur primaire")
```
Sortie :
```text
vert n'est pas une couleur primaire
```
(Au chapitre 04, tu pourras écrire encore plus simplement `if couleur in ("rouge", "bleu", "jaune"):`.)
### Piège 7 — Un `else` orphelin
```python
x = 3
if x > 0:
print("positif")
print("test terminé")
else:
print("négatif ou nul")
```
```text
File "conditions.py", line 5
else:
^^^^
SyntaxError: invalid syntax
```
Le `print` non indenté a « fermé » le `if` : le `else` n'a plus de `if` auquel se rattacher. Même erreur si un `elif` apparaît sans `if` avant lui.
**Correction :** le `else` doit suivre **immédiatement** le bloc du `if` (ou du dernier `elif`), au même niveau d'indentation que le `if`.
### Piège 8 — Des conditions dans le mauvais ordre
Comme vu à la section 5, avec `elif`, la **première** condition vraie gagne :
```python
vitesse = 150
if vitesse > 90:
print("Amende légère")
elif vitesse > 130:
print("Retrait de permis")
```
Sortie :
```text
Amende légère
```
**Correction :** teste d'abord le cas le plus restrictif (`vitesse > 130`), puis `vitesse > 90`.
---
## Exercices
### Exercice 1 ⭐ — Positif, négatif ou nul
Écris un programme qui demande un nombre (à virgule possible) et indique s'il est positif, négatif ou nul.
Exemple d'exécution :
```text
Un nombre : -4.5
-4.5 est négatif.
```
### Exercice 2 ⭐ — Le plus grand de trois
Écris un programme qui demande trois nombres entiers et affiche le plus grand, **sans utiliser** la fonction `max()`.
Exemple d'exécution :
```text
Premier nombre : 12
Deuxième nombre : 45
Troisième nombre : 7
Le plus grand est 45.
```
### Exercice 3 ⭐ — Tarif du cinéma
Un cinéma pratique les tarifs suivants : moins de 14 ans : 5 € ; de 14 à 25 ans inclus : 7.5 € ; de 26 à 64 ans : 11 € ; 65 ans et plus : 8 €. Écris un programme qui demande l'âge et affiche le tarif avec 2 décimales.
Exemple d'exécution :
```text
Âge : 25
Tarif : 7.50 €
```
### Exercice 4 ⭐⭐ — Année bissextile
Une année est **bissextile** si elle est divisible par 4 mais pas par 100, **ou** si elle est divisible par 400. Écris un programme qui demande une année et indique si elle est bissextile, en une seule condition combinant `and`, `or` et `%`.
Exemples d'exécution :
```text
Année : 2024
2024 est bissextile.
```
```text
Année : 1900
1900 n'est pas bissextile.
```
(2000 est bissextile, 2023 ne l'est pas.)
### Exercice 5 ⭐⭐ — Calcul de l'IMC
L'indice de masse corporelle (IMC) se calcule ainsi : IMC = poids / taille², avec le poids en kg et la taille en mètres. Écris un programme qui demande le poids et la taille, affiche l'IMC avec une décimale, puis l'interprétation :
| IMC | Interprétation |
|---|---|
| moins de 18.5 | insuffisance pondérale |
| de 18.5 à moins de 25 | corpulence normale |
| de 25 à moins de 30 | surpoids |
| 30 et plus | obésité |
Le programme doit refuser une taille négative ou nulle avec un message d'erreur.
Exemple d'exécution :
```text
Poids (kg) : 70
Taille (m) : 1.75
IMC : 22.9 (corpulence normale)
```
### Exercice 6 ⭐⭐ — Le jour de la semaine
Écris un programme qui demande un numéro de jour entre 1 et 7 et affiche le nom du jour correspondant (1 = lundi) ainsi que « (week-end) » pour le samedi et le dimanche. Utilise `match`/`case`. Un numéro invalide affiche un message d'erreur.
Exemples d'exécution :
```text
Numéro du jour (1-7) : 6
samedi (week-end)
```
```text
Numéro du jour (1-7) : 9
Numéro invalide : 9
```
### Exercice 7 ⭐⭐⭐ — Le type de triangle
Écris un programme qui demande les longueurs des trois côtés d'un triangle (des entiers) et affiche :
- `Ce n'est pas un triangle.` si une longueur est inférieure ou égale à 0, ou si un côté est plus grand ou égal à la somme des deux autres ;
- sinon, `Triangle équilatéral.`, `Triangle isocèle.` ou `Triangle quelconque.` ;
- et, en plus, ` Il est rectangle.` sur la même ligne si le triangle est rectangle (théorème de Pythagore : le carré du plus grand côté est égal à la somme des carrés des deux autres).
Exemples d'exécution :
```text
Côté a : 3
Côté b : 4
Côté c : 5
Triangle quelconque. Il est rectangle.
```
```text
Côté a : 2
Côté b : 2
Côté c : 9
Ce n'est pas un triangle.
```
### Exercice 8 ⭐⭐⭐ — Le distributeur de boissons
Un distributeur propose : `1` café (1.20 €), `2` thé (1.00 €), `3` chocolat (1.50 €). Écris un programme qui :
1. affiche le menu et demande le numéro de la boisson ;
2. si le choix est invalide, affiche « Choix invalide. » et s'arrête là ;
3. sinon, demande le montant inséré et :
- si le montant est insuffisant, indique combien il manque ;
- sinon, sert la boisson et indique la monnaie rendue (ou « Compte juste, merci ! » si elle vaut 0).
Pour éviter les soucis d'imprécision des floats, fais tous les calculs **en centimes** (entiers) : `round(montant * 100)`.
Exemple d'exécution :
```text
1. Café (1.20 €)
2. Thé (1.00 €)
3. Chocolat (1.50 €)
Ton choix : 3
Montant inséré (€) : 2
Voici ton chocolat. Monnaie rendue : 0.50 €
```
---
## Mini-projet
### Le quiz de culture générale
**Objectif :** écrire un programme `quiz.py` qui pose 5 questions à l'utilisateur, compte les points et affiche une appréciation finale.
**Cahier des charges :**
1. Le programme affiche un titre et demande le prénom du joueur. Si le prénom est vide, il utilise « Joueur mystère ».
2. Il pose **5 questions** de types variés :
- au moins une question à réponse **texte**, tolérante aux majuscules et aux espaces (`strip().lower()`), et qui accepte plusieurs variantes de réponse (par exemple « paris » ou « la ville de paris ») ;
- au moins une question à réponse **numérique** entière ;
- au moins une question à réponse numérique où une réponse **approchée** rapporte la moitié des points (par exemple, à 5 ans près pour une date) ;
- au moins une question **à choix multiples** (a, b, c, d) traitée avec `match`/`case`, qui affiche un message spécifique pour chaque mauvaise réponse ;
- au moins une question **vrai/faux**.
3. Après chaque réponse, le programme affiche « Bonne réponse ! » ou la bonne réponse.
4. Le score (qui peut contenir des demi-points) est affiché à la fin sur 5, avec une appréciation selon le score : 5 → « Parfait ! », 4 ou plus → « Excellent », 2.5 ou plus → « Pas mal », moins → « Révise un peu ».
5. Le score est affiché sans décimale inutile : `4` plutôt que `4.0`, mais `3.5` reste `3.5` (indice : le format `:g`).
**Exemple d'exécution :**
```text
===== QUIZ DE CULTURE GÉNÉRALE =====
Ton prénom : Léa
Question 1 : Quelle est la capitale de la France ?
> Paris
Bonne réponse !
Question 2 : Combien de pattes a une araignée ?
> 8
Bonne réponse !
Question 3 : En quelle année l'homme a-t-il marché sur la Lune ?
> 1970
Presque ! C'était en 1969. (+0.5)
Question 4 : Quel est le plus grand océan ?
a) Atlantique b) Indien c) Pacifique d) Arctique
> a
Non, l'Atlantique est le deuxième. C'était le Pacifique.
Question 5 : Vrai ou faux : la tomate est un fruit ?
> vrai
Bonne réponse !
Léa, ton score : 3.5 / 5 — Pas mal
```
**Étapes suggérées :**
1. Écris l'en-tête, la saisie du prénom (avec la valeur par défaut) et initialise `score = 0`.
2. Écris la question 1 et teste-la avec plusieurs variantes de réponse (`PARIS`, ` paris `, `Lyon`).
3. Ajoute les questions une par une, en testant à chaque fois les bonnes et les mauvaises réponses.
4. Termine par l'appréciation finale avec une chaîne `if`/`elif`/`else`.
**Pistes d'amélioration :**
- Ajoute un « mode difficile » choisi au début, qui change les questions posées.
- Pour la question numérique, vérifie que la saisie contient bien des chiffres avec `reponse.isdigit()` avant de convertir, pour éviter le plantage (une approche plus robuste avec `try`/`except` sera vue au chapitre 08).
- Au chapitre 03, tu pourras proposer de rejouer, ou donner plusieurs essais par question ; au chapitre 04, stocker les questions dans une liste.
---
## Corrigés
<details>
<summary>Corrigé de l'exercice 1</summary>
```python
# Positif, négatif ou nul
nombre = float(input("Un nombre : "))
if nombre > 0:
print(f"{nombre} est positif.")
elif nombre < 0:
print(f"{nombre} est négatif.")
else:
print(f"{nombre} est nul.")
```
Exemple d'exécution :
```text
Un nombre : -4.5
-4.5 est négatif.
```
Le `else` n'a pas besoin de condition : si le nombre n'est ni positif ni négatif, il est forcément nul.
</details>
<details>
<summary>Corrigé de l'exercice 2</summary>
```python
# Le plus grand de trois nombres, sans max()
a = int(input("Premier nombre : "))
b = int(input("Deuxième nombre : "))
c = int(input("Troisième nombre : "))
if a >= b and a >= c:
plus_grand = a
elif b >= c: # ici, on sait déjà que a n'est pas le plus grand
plus_grand = b
else:
plus_grand = c
print(f"Le plus grand est {plus_grand}.")
```
Exemple d'exécution :
```text
Premier nombre : 12
Deuxième nombre : 45
Troisième nombre : 7
Le plus grand est 45.
```
Autre approche, très utile pour la suite : supposer que le premier est le plus grand, puis corriger si besoin.
```python
a, b, c = 12, 45, 7
plus_grand = a
if b > plus_grand:
plus_grand = b
if c > plus_grand:
plus_grand = c
print(f"Le plus grand est {plus_grand}.")
```
Sortie :
```text
Le plus grand est 45.
```
Ici, on utilise des `if` **indépendants** (pas de `elif`) : chaque nombre doit être comparé. Cette technique se généralisera très bien avec les boucles au chapitre 03.
</details>
<details>
<summary>Corrigé de l'exercice 3</summary>
```python
# Tarif du cinéma selon l'âge
age = int(input("Âge : "))
if age < 14:
tarif = 5
elif age <= 25:
tarif = 7.5
elif age < 65:
tarif = 11
else:
tarif = 8
print(f"Tarif : {tarif:.2f} €")
```
Exemple d'exécution :
```text
Âge : 25
Tarif : 7.50 €
```
Grâce à `elif`, inutile d'écrire `14 <= age <= 25` : si on arrive au deuxième test, on sait déjà que `age >= 14`. Attention aux bornes : « de 14 à 25 ans inclus » se traduit par `<= 25`.
</details>
<details>
<summary>Corrigé de l'exercice 4</summary>
```python
# Année bissextile
annee = int(input("Année : "))
if (annee % 4 == 0 and annee % 100 != 0) or annee % 400 == 0:
print(f"{annee} est bissextile.")
else:
print(f"{annee} n'est pas bissextile.")
```
Exemples d'exécution :
```text
Année : 2024
2024 est bissextile.
```
```text
Année : 1900
1900 n'est pas bissextile.
```
Les parenthèses ne sont pas strictement nécessaires ici (`and` est prioritaire sur `or`), mais elles rendent la règle bien plus lisible. Vérifie aussi : 2000 → divisible par 400 → bissextile ; 2023 → pas divisible par 4 → non.
</details>
<details>
<summary>Corrigé de l'exercice 5</summary>
```python
# Calcul de l'IMC
poids = float(input("Poids (kg) : "))
taille = float(input("Taille (m) : "))
if taille <= 0:
print("Erreur : la taille doit être strictement positive.")
else:
imc = poids / taille ** 2
if imc < 18.5:
interpretation = "insuffisance pondérale"
elif imc < 25:
interpretation = "corpulence normale"
elif imc < 30:
interpretation = "surpoids"
else:
interpretation = "obésité"
print(f"IMC : {imc:.1f} ({interpretation})")
```
Exemple d'exécution :
```text
Poids (kg) : 70
Taille (m) : 1.75
IMC : 22.9 (corpulence normale)
```
On vérifie la taille **avant** de calculer, sinon une taille de 0 provoquerait une `ZeroDivisionError`. Tout le calcul est dans le bloc `else`, d'où le deuxième niveau d'indentation. `taille ** 2` est calculé avant la division, car `**` est prioritaire.
</details>
<details>
<summary>Corrigé de l'exercice 6</summary>
```python
# Nom du jour avec match/case
numero = int(input("Numéro du jour (1-7) : "))
match numero:
case 1:
print("lundi")
case 2:
print("mardi")
case 3:
print("mercredi")
case 4:
print("jeudi")
case 5:
print("vendredi")
case 6:
print("samedi (week-end)")
case 7:
print("dimanche (week-end)")
case _:
print(f"Numéro invalide : {numero}")
```
Exemples d'exécution :
```text
Numéro du jour (1-7) : 6
samedi (week-end)
```
```text
Numéro du jour (1-7) : 9
Numéro invalide : 9
```
On convertit avec `int()`, car `case 6:` compare à l'**entier** 6 : la chaîne `"6"` ne correspondrait à aucun cas. Au chapitre 04, une liste des noms de jours permettra d'écrire ce programme en trois lignes.
</details>
<details>
<summary>Corrigé de l'exercice 7</summary>
```python
# Type de triangle
a = int(input("Côté a : "))
b = int(input("Côté b : "))
c = int(input("Côté c : "))
if a <= 0 or b <= 0 or c <= 0 or a >= b + c or b >= a + c or c >= a + b:
print("Ce n'est pas un triangle.")
else:
# Nature du triangle
if a == b == c:
message = "Triangle équilatéral."
elif a == b or b == c or a == c:
message = "Triangle isocèle."
else:
message = "Triangle quelconque."
# Est-il rectangle ? On cherche le plus grand côté.
if a >= b and a >= c:
rectangle = a ** 2 == b ** 2 + c ** 2
elif b >= c:
rectangle = b ** 2 == a ** 2 + c ** 2
else:
rectangle = c ** 2 == a ** 2 + b ** 2
if rectangle:
message = message + " Il est rectangle."
print(message)
```
Exemples d'exécution :
```text
Côté a : 3
Côté b : 4
Côté c : 5
Triangle quelconque. Il est rectangle.
```
```text
Côté a : 2
Côté b : 2
Côté c : 9
Ce n'est pas un triangle.
```
Points importants :
- l'équilatéral est testé **avant** l'isocèle (un triangle équilatéral a aussi deux côtés égaux) ;
- `a == b == c` est une comparaison enchaînée ;
- `rectangle = a ** 2 == b ** 2 + c ** 2` stocke directement le **booléen** résultant de la comparaison, sans `if` ;
- comme les côtés sont des entiers, la comparaison avec `==` est exacte (pas de souci de floats).
</details>
<details>
<summary>Corrigé de l'exercice 8</summary>
```python
# Distributeur de boissons (calculs en centimes)
print("1. Café (1.20 €)")
print("2. Thé (1.00 €)")
print("3. Chocolat (1.50 €)")
choix = input("Ton choix : ").strip()
match choix:
case "1":
boisson, prix_centimes = "café", 120
case "2":
boisson, prix_centimes = "thé", 100
case "3":
boisson, prix_centimes = "chocolat", 150
case _:
boisson, prix_centimes = "", 0
if boisson == "":
print("Choix invalide.")
else:
montant = float(input("Montant inséré (€) : "))
montant_centimes = round(montant * 100)
if montant_centimes < prix_centimes:
manque = prix_centimes - montant_centimes
print(f"Montant insuffisant : il manque {manque / 100:.2f} €.")
else:
rendu = montant_centimes - prix_centimes
if rendu == 0:
print(f"Voici ton {boisson}. Compte juste, merci !")
else:
print(f"Voici ton {boisson}. Monnaie rendue : {rendu / 100:.2f} €")
```
Exemple d'exécution :
```text
1. Café (1.20 €)
2. Thé (1.00 €)
3. Chocolat (1.50 €)
Ton choix : 3
Montant inséré (€) : 2
Voici ton chocolat. Monnaie rendue : 0.50 €
```
Autre exécution :
```text
1. Café (1.20 €)
2. Thé (1.00 €)
3. Chocolat (1.50 €)
Ton choix : 1
Montant inséré (€) : 1
Montant insuffisant : il manque 0.20 €.
```
On garde le choix sous forme de **texte** : ainsi, une saisie comme `abc` tombe simplement dans le `case _` au lieu de faire planter `int()`. Travailler en centimes évite les surprises : `1.5 - 1.2` donne `0.30000000000000004`, alors que `150 - 120` donne exactement `30`.
</details>
<details>
<summary>Corrigé du mini-projet</summary>
```python
# quiz.py — Quiz de culture générale en 5 questions
print("===== QUIZ DE CULTURE GÉNÉRALE =====")
prenom = input("Ton prénom : ").strip()
if not prenom:
prenom = "Joueur mystère"
score = 0
# --- Question 1 : réponse texte, plusieurs variantes acceptées ---
print()
print("Question 1 : Quelle est la capitale de la France ?")
reponse = input("> ").strip().lower()
if reponse == "paris" or reponse == "la ville de paris":
print("Bonne réponse !")
score += 1
else:
print("Raté, c'était Paris.")
# --- Question 2 : réponse numérique exacte ---
print()
print("Question 2 : Combien de pattes a une araignée ?")
reponse = int(input("> "))
if reponse == 8:
print("Bonne réponse !")
score += 1
else:
print("Raté, une araignée a 8 pattes.")
# --- Question 3 : réponse numérique approchée ---
print()
print("Question 3 : En quelle année l'homme a-t-il marché sur la Lune ?")
reponse = int(input("> "))
if reponse == 1969:
print("Bonne réponse !")
score += 1
elif abs(reponse - 1969) <= 5:
print("Presque ! C'était en 1969. (+0.5)")
score += 0.5
else:
print("Raté, c'était en 1969.")
# --- Question 4 : choix multiples avec match ---
print()
print("Question 4 : Quel est le plus grand océan ?")
print(" a) Atlantique b) Indien c) Pacifique d) Arctique")
reponse = input("> ").strip().lower()
match reponse:
case "c":
print("Bonne réponse !")
score += 1
case "a":
print("Non, l'Atlantique est le deuxième. C'était le Pacifique.")
case "b":
print("Non, l'océan Indien est le troisième. C'était le Pacifique.")
case "d":
print("Non, l'Arctique est le plus petit ! C'était le Pacifique.")
case _:
print("Réponse invalide. C'était le Pacifique.")
# --- Question 5 : vrai / faux ---
print()
print("Question 5 : Vrai ou faux : la tomate est un fruit ?")
reponse = input("> ").strip().lower()
if reponse == "vrai" or reponse == "v":
print("Bonne réponse !")
score += 1
else:
print("Raté : botaniquement, la tomate est bien un fruit.")
# --- Résultat final ---
if score == 5:
appreciation = "Parfait !"
elif score >= 4:
appreciation = "Excellent"
elif score >= 2.5:
appreciation = "Pas mal"
else:
appreciation = "Révise un peu"
print()
print(f"{prenom}, ton score : {score:g} / 5 — {appreciation}")
```
Exemple d'exécution (mêmes réponses que dans l'énoncé) :
```text
===== QUIZ DE CULTURE GÉNÉRALE =====
Ton prénom : Léa
Question 1 : Quelle est la capitale de la France ?
> Paris
Bonne réponse !
Question 2 : Combien de pattes a une araignée ?
> 8
Bonne réponse !
Question 3 : En quelle année l'homme a-t-il marché sur la Lune ?
> 1970
Presque ! C'était en 1969. (+0.5)
Question 4 : Quel est le plus grand océan ?
a) Atlantique b) Indien c) Pacifique d) Arctique
> a
Non, l'Atlantique est le deuxième. C'était le Pacifique.
Question 5 : Vrai ou faux : la tomate est un fruit ?
> vrai
Bonne réponse !
Léa, ton score : 3.5 / 5 — Pas mal
```
Remarques :
- `if not prenom:` détecte une chaîne vide (section 8 du cours).
- `abs(reponse - 1969) <= 5` signifie « à 5 ans près » : `abs()` donne l'écart sans se soucier du signe (1964 et 1974 sont acceptés).
- Le `elif` de la question 3 n'est testé que si la réponse n'est pas exacte : une réponse exacte ne rapporte donc pas 1.5 point.
- `{score:g}` affiche `4` pour `4` ou `4.0`, et `3.5` pour `3.5`.
- On remarque beaucoup de répétitions d'une question à l'autre : les boucles (chapitre 03), les listes (chapitre 04) et les fonctions (chapitre 05) permettront de les éliminer.
</details>
---
## Récapitulatif
**Points clés :**
- Les opérateurs de comparaison `==`, `!=`, `<`, `>`, `<=`, `>=` renvoient un booléen. Ne confonds pas `=` (affectation) et `==` (comparaison).
- Structure : `if condition:` puis un bloc **indenté de 4 espaces**. `elif` et `else` sont facultatifs ; `else` n'a jamais de condition.
- Dans une chaîne `if`/`elif`/`else`, **seul le premier bloc dont la condition est vraie** s'exécute : l'ordre des conditions compte.
- `and` (toutes vraies), `or` (au moins une vraie), `not` (inverse). Priorité : `not` > `and` > `or` ; mets des parenthèses pour être clair.
- `a <= x <= b` permet d'écrire un encadrement.
- `0`, `0.0`, `""` sont considérés comme faux ; `if texte:` signifie « si le texte n'est pas vide ».
- `in` teste si un morceau de texte est contenu dans une chaîne.
- `valeur1 if condition else valeur2` choisit entre deux valeurs sur une ligne.
- `match valeur:` / `case ...:` / `case _:` compare une valeur à plusieurs cas ; `|` regroupe plusieurs valeurs, `case x if ...:` ajoute une garde.
- Convertis toujours les saisies numériques avant de les comparer, et normalise les saisies texte avec `.strip().lower()`.
**Checklist de validation :**
- [ ] Je sais écrire une condition avec `if`, `elif` et `else`, avec les `:` et l'indentation correcte.
- [ ] Je sais prédire quel bloc s'exécute dans une chaîne de `elif`, et j'ordonne mes conditions en conséquence.
- [ ] Je sais combiner des conditions avec `and`, `or`, `not` et je connais le piège `x == "a" or "b"`.
- [ ] Je sais utiliser une comparaison enchaînée et l'expression `a if condition else b`.
- [ ] Je sais écrire un `match`/`case` avec un cas par défaut `_`.
- [ ] Mes programmes ne plantent plus sur une division par zéro grâce à une vérification préalable.
- [ ] J'ai réalisé le quiz et testé chaque question avec une bonne et une mauvaise réponse.
---
[← Chapitre précédent](01-variables-types-operateurs.md) | [Sommaire](README.md) | [Chapitre suivant →](03-boucles.md)