Leçon 02-2 — Opérateurs logiques : combiner des conditions
1. Objectif
À la fin de cette leçon tu sauras : combiner plusieurs conditions avec and, or et not ; prédire le résultat d'une expression logique grâce aux tables de vérité et aux règles de priorité ; expliquer ce qu'est une valeur « truthy »/« falsy » ; et repérer au premier coup d'œil le piège if x == 1 or 2: — l'erreur de logique la plus célèbre du niveau.
2. Pourquoi c'est important
Les conditions réelles sont presque toujours composées : « majeur ET muni d'un billet », « samedi OU dimanche », « PAS déjà inscrit ». Sans and/or/not, tu empiles des if imbriqués illisibles (tu l'as senti dans l'exercice 9-c de la leçon 02-1). Et c'est dans les combinaisons que naissent les bugs de logique : une condition toujours vraie ne produit aucune erreur, juste un programme qui fait n'importe quoi. Savoir lire une expression logique comme Python la lit, c'est la moitié du débogage du niveau 2.
3. Explication simple
Trois mots-clés, en anglais courant dans le code :
age = 25
income = 30000
print(age >= 21 and income >= 20000) # True : les DEUX sont vraies
print(age < 18 or income >= 20000) # True : au moins UNE est vraie
print(not age < 18) # True : inverse la condition
Les tables de vérité complètes (à connaître, elles sont courtes) :
a | b | a and b | a or b |
|---|---|---|---|
True | True | True | True |
True | False | False | True |
False | True | False | True |
False | False | False | False |
Et not : not True → False, not False → True.
Moyens mnémotechniques : and est exigeant (tout doit être vrai) ; or est facile à contenter (une seule suffit — et le or de Python est inclusif : les deux vraies, c'est vrai aussi) ; not retourne la crêpe.
4. Explication approfondie
a) Priorité des opérateurs. Comme * passe avant + en maths, il y a un ordre : not d'abord, puis and, puis or. Et les comparaisons (==, <…) sont évaluées avant tout ça.
print(True or False and False) # True : and d'abord → True or False → True
print((True or False) and False) # False : les parenthèses changent tout
print(not True and False) # False : not True → False, puis False and False
print(not (True and False)) # True : d'abord la parenthèse
Règle de vie : dès qu'une expression mélange and et or, mets des parenthèses, même quand elles sont facultatives. Ton lecteur (toi dans un mois) ne connaît pas les priorités par cœur.
b) Truthy / falsy : tout a une valeur de vérité. Dans un contexte de condition, Python accepte n'importe quelle valeur, pas seulement True et False, et la convertit avec bool() :
print(bool(0), bool(1), bool(-1), bool(0.0)) # False True True False
print(bool(""), bool("0"), bool(" "), bool("False")) # False True True True
Retiens la règle courte : sont falsy le zéro (0, 0.0), la chaîne vide "" et False lui-même (⏩ au niveau 3 s'ajouteront la liste vide et None). Tout le reste est truthy — y compris "0", " " (une espace) et même la chaîne "False" : une chaîne non vide est vraie, peu importe son contenu. D'où l'idiome courant if name: pour dire « si l'utilisateur a tapé quelque chose ».
c) LE piège du niveau : if x == 1 or 2:. En français on dit « si x vaut 1 ou 2 » ; en Python ça se découpe autrement :
x = 5
print(x == 1 or 2) # affiche : 2 (truthy → le if serait TOUJOURS pris)
print(x == 1 or x == 2) # False ← la version correcte
Python lit (x == 1) or (2) : la comparaison vaut False, donc or renvoie son deuxième opérande, 2 — qui est truthy. La condition est vraie pour n'importe quel x, sans le moindre message d'erreur. Chaque valeur a droit à sa comparaison complète : x == 1 or x == 2.
(Tu remarques que or a renvoyé 2, pas True : and/or renvoient l'un de leurs opérandes, pas forcément un bool. C'est pour ça que print(0 or "default") affiche default — idiome que tu recroiseras dans du vrai code. Pour l'instant, sache-le sans t'en servir.)
d) L'évaluation court-circuit. Python s'arrête dès que le résultat est connu : dans a and b, si a est faux, b n'est jamais évalué ; dans a or b, si a est vrai, pareil. Ce n'est pas qu'une optimisation — c'est un outil de protection :
n = 0
if n != 0 and 100 / n > 2: # la division n'est JAMAIS tentée si n vaut 0
print("grand")
else:
print("nul ou petit")
Sortie : nul ou petit. Inverse l'ordre (100 / n > 2 and n != 0) et le programme plante sur ZeroDivisionError: division by zero. L'ordre des opérandes d'un and peut donc être vital : le garde-fou d'abord.
Difficulté honnête : la mécanique (and/or/not) s'apprend vite. Ce qui demande de la pratique : penser comme Python découpe (x == 1 or 2), et les valeurs truthy contre-intuitives ("0" est vraie, "False" est vraie). Prévois une session pour les sections 1–7 et une pour les exercices — et accepte de te faire piéger une fois ou deux, c'est le passage obligé.
5. Exemples commentés
Exemple 1 — and : plusieurs exigences à la fois
age = 25
income = 30000
if age >= 21 and income >= 20000:
print("Pret accorde")
else:
print("Pret refuse")
Sortie : Pret accorde. Les deux conditions sont vérifiées indépendamment, chacune complète (variable + opérateur + valeur).
Exemple 2 — or correct : chaque option a sa comparaison
day = 6
if day == 6 or day == 7:
print("Week-end !")
else:
print("Semaine")
Sortie : Week-end !. Compare avec la version piégée de la section 4-c.
Exemple 3 — not et la lisibilité
is_raining = False
if not is_raining:
print("On sort !")
Sortie : On sort !. not brille avec des variables bool bien nommées. Mais préfère if x != 5: à if not x == 5: — même sens, plus direct.
Exemple 4 — l'intervalle, trois écritures équivalentes
n = 7
print(1 < n and n < 10) # True — la version explicite
print(1 < n < 10) # True — le chaînage (vu en 02-1), le plus lisible
Exemple 5 — truthy en pratique : la saisie vide
name = input("Ton prenom ? ")
if name: # truthy : vrai si la chaîne n'est PAS vide
print(f"Bonjour {name} !")
else:
print("Tu n'as rien tape.")
Session (l'utilisateur appuie juste sur Entrée) :
Ton prenom ?
Tu n'as rien tape.
Exemple 6 — le court-circuit qui protège une division (celui de la section 4-d — tape-le, puis inverse l'ordre des opérandes pour voir le ZeroDivisionError de tes propres yeux.)
6. Erreurs fréquentes
1) if x == 1 or 2: — la condition toujours vraie
choice = input("Ton choix (1 ou 2) ? ")
if choice == "1" or "2":
print("Choix valide") # affiché même si l'utilisateur tape "9" !
Aucune erreur, le menu accepte tout. Python lit (choice == "1") or ("2"), et "2" est truthy. Correction : if choice == "1" or choice == "2":. Relis chaque or/and en vérifiant que chaque côté est une condition complète.
2) Inverser la priorité and/or de tête
# voulu : week-end OU (semaine et férié)
day = 2
holiday = False
if day == 6 or day == 7 and holiday:
print("Repos")
Ici ça « marche » (pas de repos un mardi normal)… mais par chance : and étant prioritaire, la condition se lit day == 6 or (day == 7 and holiday) — un samedi sans férié passe, ce qui était voulu, alors qu'on aurait juré avoir écrit (day == 6 or day == 7) and holiday. Les deux lectures divergent selon les valeurs : bug intermittent, le pire genre. Correction : parenthèses, systématiquement, dès que les deux opérateurs cohabitent.
3) Croire que "0" ou "False" sont faux
answer = input("Continuer ? (tape 0 pour arreter) ")
if answer:
print("On continue !") # affiché MÊME si l'utilisateur tape 0
answer vaut "0" — une chaîne non vide, donc truthy. Le test truthy vérifie « vide ou pas », rien d'autre. Pour tester le contenu : if answer != "0":.
4) Écrire && ou || comme dans d'autres langages
x = 5
y = 10
if x > 0 && y > 0:
print("both positive")
# SyntaxError: invalid syntax
En Python c'est and, or, not — des mots, pas des symboles. (Le message ne suggère rien ici : à toi de reconnaître le réflexe venu de JavaScript/C.)
5) Mettre le garde-fou du mauvais côté du and
n = 0
if 100 / n > 2 and n != 0:
print("grand")
# ZeroDivisionError: division by zero
Le court-circuit évalue de gauche à droite : la division explose avant que n != 0 ne soit consulté. Correction : if n != 0 and 100 / n > 2:.
7. Lire les messages d'erreur
La spécialité de cette leçon est justement l'absence de message : une condition toujours vraie ou toujours fausse s'exécute sans broncher (erreurs 1 à 3 ci-dessus). Quand un if prend systématiquement la même branche, le réflexe est print() sur la condition — méthode complète en leçon 02-5.
Les deux erreurs bruyantes possibles :
SyntaxError — le && importé d'un autre langage
File "c:\Users\jtron\Claude\learnpython\logic.py", line 3
if x > 0 && y > 0:
^
SyntaxError: invalid syntax
Pas de Traceback : rien n'a été exécuté. Le ^ pointe le premier & — quand un SyntaxError ne suggère rien, regarde le caractère pointé et demande-toi « est-ce du Python, ça ? ».
ZeroDivisionError — le court-circuit dans le mauvais sens
Traceback (most recent call last):
File "c:\Users\jtron\Claude\learnpython\lessons\level-02-conditions-boucles\exercices\mes-reponses\ex-02-2-e.py", line 2, in <module>
if 100 / n > 2 and n != 0:
~~~~^~~
ZeroDivisionError: division by zero
- Dernière ligne : diviser par zéro est mathématiquement indéfini, Python refuse. Le
~~~~^~~souligne précisément100 / n. - La présence du
and n != 0DANS la ligne fautive est l'indice-clé : le garde-fou existe, mais à droite, donc trop tard. Déplace-le à gauche.
8. Exercices — faciles
Règles : nouveau fichier par exercice dans
lessons/level-02-conditions-boucles/exercices/mes-reponses/, nomméex-02-2-a.py, etc. Tape tout à la main. Prédis la sortie AVANT d'exécuter.
a) Sans exécuter, prédis les quatre sorties (attention aux priorités), puis vérifie :
print(True and False)
print(not False)
print(True or False and False)
print(not True or True)
b) Sans exécuter, prédis les six sorties, puis vérifie et note dans ton journal celles qui t'ont surpris :
print(bool(""))
print(bool("False"))
print(bool(0))
print(bool(0.0))
print(bool(-5))
print(bool(" "))
c) Demande une température (en °C, float) et affiche Baignade ! si elle est entre 24 et 32 inclus, sinon Trop froid en dessous et Trop chaud au-dessus. Écris la condition de baignade UNE fois avec and, une fois en chaînage 24 <= t <= 32 — vérifie que les deux versions réagissent pareil aux bornes exactes (24, 32).
9. Exercices — moyens
a) Ce menu accepte n'importe quoi. Explique le bug sans exécuter, prédis ce qu'affiche une saisie 9, puis corrige :
choice = input("Ton choix (1 ou 2) ? ")
if choice == "1" or "2":
print("Choix valide")
else:
print("Choix invalide")
b) Année bissextile : demande une année et affiche si elle est bissextile. Règle exacte : divisible par 4 et pas par 100 — ou divisible par 400. Contraintes : % pour la divisibilité, parenthèses obligatoires, et vérifie tes quatre cas de contrôle : 2024 (oui), 2023 (non), 1900 (non !), 2000 (oui).
c) Connexion : demande un identifiant puis un mot de passe. Accès accordé si (user == "admin" et password == "s3cret") ou si user == "invite" (peu importe le mot de passe). Une seule chaîne if/else, parenthèses explicites. Teste les quatre combinaisons : admin/bon, admin/mauvais, invite/n'importe quoi, inconnu/n'importe quoi.
10. Exercices — difficiles
a) Lois de De Morgan. Le programme ci-dessous doit afficher quatre lignes True (une par combinaison de a et b) prouvant que not (a and b) équivaut à not a or not b :
# pour chaque combinaison, afficher : not (a and b) == (not a or not b)
Écris-le (quatre blocs de deux assignations + un print, ou mieux si tu vois comment ⏩ les boucles arrivent en 02-3/02-4). Puis fais pareil pour la deuxième loi : not (a or b) équivaut à not a and not b. Enfin, utilise-la pour réécrire SANS not global : not (age >= 18 and city == "Nice").
- Indice 1 : les combinaisons à couvrir sont exactement les quatre lignes de la table de vérité de la section 3.
- Indice 2 :
print((not (a and b)) == (not a or not b))affiche directement le verdict de l'équivalence pour la combinaison en cours — parenthèses autour du premiernot (...)obligatoires, sinon Python litnot (X == Y), autre chose. - Indice 3 : quand
nottraverse une parenthèse, chaque morceau s'inverse ET le connecteur bascule (and↔or) :age < 18 or city != "Nice".
11. Mini-projet lié — « contrôle d'accès du club »
Écris door_policy.py : le programme pose trois questions —
Age ? 17
Membre du club ? (oui/non) non
Accompagne d'un adulte ? (oui/non) oui
— puis applique la politique d'entrée : entrée accordée si la personne a 18 ans ou plus ou si elle est accompagnée d'un adulte ; MAIS un membre du club entre dans tous les cas ; ET en dessous de 12 ans, refus absolu, membre ou pas. Affiche ACCES ACCORDE ou ACCES REFUSE, plus une ligne expliquant la raison.
Contraintes : les réponses oui/non sont comparées à "oui" (is_member = answer == "oui" te donne un vrai bool) ; la décision finale tient dans UNE expression logique parenthésée, stockée dans une variable allowed avant le if. Écris d'abord la politique en français sur papier, traduis ensuite. Teste au minimum : 17/non/oui, 17/oui/non, 11/oui/oui, 25/non/non. Durée cible : 30–40 min.
12. Correction / méthode de correction
Les solutions détaillées sont dans solutions/serie-02-2-solutions.md.
Méthode : (1) finis TOUS les exercices avant d'ouvrir les solutions ; (2) compare le raisonnement, pas seulement le code ; (3) pour chaque écart, une ligne de journal : « je pensais X, en fait Y » ; (4) refais de tête l'exercice raté, le lendemain.
13. À retenir
and: tout doit être vrai.or: une suffit (inclusif).not: inverse.- Priorité : comparaisons, puis
not, puisand, puisor— mais mets des parenthèses dès queandetorcohabitent. if x == 1 or 2:est toujours vrai — chaque option exige sa comparaison complète :x == 1 or x == 2.- Falsy :
0,0.0,"",False. Tout le reste est truthy — y compris"0"et"False". if name:= « si la chaîne n'est pas vide » : idiome à connaître.- Court-circuit :
and/ors'arrêtent dès que le résultat est connu — place le garde-fou (n != 0) à gauche duand. &&/||n'existent pas en Python.- Les bugs de cette leçon sont silencieux : pas de traceback, une branche toujours prise. Réflexe :
print()la condition.
14. Questions de révision
- Récite les tables de vérité de
andetor. Leorde Python est-il exclusif ou inclusif ? - Que vaut
True or False and False, et pourquoi ? Et avec des parenthèses autour duor? - Pourquoi
if x == 1 or 2:est-il toujours vrai ? Comment Python découpe-t-il exactement cette expression ? - Cite les quatre valeurs falsy vues dans cette leçon.
"0"est-il truthy ou falsy ? Et" "? - Qu'est-ce que l'évaluation court-circuit ? Donne un cas où l'ordre des opérandes d'un
andévite un plantage. - Pourquoi
bool("False")vaut-ilTrue? - Comment réécrire
not (a or b)sans parenthèses (loi de De Morgan) ? - Les bugs typiques de cette leçon produisent-ils des tracebacks ? Comment les détecte-t-on alors ?
15. Checklist de compréhension
- Je récite les deux tables de vérité sans hésiter.
- Je repère le piège
x == 1 or 2dans du code en quelques secondes et je sais expliquer POURQUOI c'est toujours vrai. - Je classe truthy/falsy sans me tromper :
0,"0",""," ",0.0,"False". - Je sais dans quel ordre Python évalue
not a and b or c— et je sais que je dois mettre des parenthèses au lieu de le laisser deviner. - Je peux expliquer le court-circuit avec l'exemple de la division par zéro.
- L'exercice bissextile passe mes quatre cas de contrôle (2024, 2023, 1900, 2000).
- Le mini-projet refuse bien un membre de 11 ans et accepte un non-membre de 17 ans accompagné.
Si une case reste vide : refais les exercices de la section correspondante demain, AVANT d'ouvrir la leçon suivante.
16. Commit conseillé
git add lessons/level-02-conditions-boucles/exercices/mes-reponses/
git commit -m "exercises: complete lesson 02-2 logical operators"
Puis, si c'est ta fin de session : entrée de journal + git push.