a = [1, 2, 3]
b = [1, 2, 3]
c = a
print("a == b :", a == b) # True : même contenu
print("a is b :", a is b) # False : objets différents
print("a is c :", a is c) # True : aliasa == b : True
a is b : False
a is c : True
Les conditions if/else et les opérateurs and/or/not semblent simples. En apparence seulement. Ce chapitre explore les subtilités que le TOSA teste massivement : court-circuit, idiomes pythoniques (le or de valeur par défaut), clause else des boucles, et rappels de priorité d’opérateurs.
is et == (rappel)Vu au chapitre précédent, on en fait un résumé pour ce chapitre :
== teste si les valeurs sont égales.is teste si les deux noms pointent vers le même objet en mémoire.a = [1, 2, 3]
b = [1, 2, 3]
c = a
print("a == b :", a == b) # True : même contenu
print("a is b :", a is b) # False : objets différents
print("a is c :", a is c) # True : aliasa == b : True
a is b : False
a is c : True
Règle pratique :
is avec None, True, False.== pour comparer des valeurs.Principe : Python évalue les opérateurs logiques de gauche à droite et s’arrête dès qu’il peut conclure.
A and B : si A est faux → ne pas évaluer B, retourner A.A or B : si A est vrai → ne pas évaluer B, retourner A.Démonstration avec une fonction qui affiche quand elle est appelée :
def verifier(x):
print(f" → vérification de {x}")
return x > 0
print("Test `and` avec échec à gauche :")
resultat = verifier(-5) and verifier(10) # verifier(10) pas exécuté !
print("Résultat :", resultat)Test `and` avec échec à gauche :
→ vérification de -5
Résultat : False
def verifier(x):
print(f" → vérification de {x}")
return x > 0
print("\nTest `or` avec succès à gauche :")
resultat = verifier(5) or verifier(10) # verifier(10) pas exécuté !
print("Résultat :", resultat)
Test `or` avec succès à gauche :
→ vérification de 5
Résultat : True
Application concrète : éviter une division par zéro.
def est_valide(x):
return x != 0 and 10 / x > 2 # la 2e partie NE S'ÉVALUE PAS si x == 0
print(est_valide(0)) # False, pas de ZeroDivisionError
print(est_valide(3)) # True
print(est_valide(10)) # False (10/10 = 1 < 2)False
True
False
and/or renvoient l’opérande, pas un booléen
Détail très testé au TOSA : and et or ne renvoient pas forcément True/False, mais l’une des deux opérandes.
A and B : renvoie A si A est falsy, sinon B.A or B : renvoie A si A est truthy, sinon B.# and : renvoie le premier falsy, sinon le dernier
print(0 and 5) # 0
print(3 and 5) # 5
print("" and "oui") # ""
# or : renvoie le premier truthy, sinon le dernier
print(0 or 5) # 5
print(3 or 5) # 3
print("" or "défaut") # "défaut"
print(None or 0 or "") # "" (tous falsy, renvoie le dernier)0
5
5
3
défaut
orUne conséquence pratique de ce comportement : on peut utiliser or pour fournir une valeur par défaut quand une variable peut être vide ou None.
def saluer(nom):
nom = nom or "inconnu" # si nom est "", None, 0... → "inconnu"
print(f"Bonjour {nom}")
saluer("Alice")
saluer("")
saluer(None)Bonjour Alice
Bonjour inconnu
Bonjour inconnu
C’est un idiome classique en Python. Équivalent plus explicite :
if not nom:
nom = "inconnu"or traite tout falsy comme défaut
Cela signifie que 0 ou False déclenchent aussi le défaut :
quantite = 0
quantite = quantite or 1 # quantite vaut maintenant 1 !
print(quantite)1
Si vous voulez seulement remplacer None, utilisez un test explicite :
if quantite is None:
quantite = 1Python évalue n’importe quelle valeur comme booléen. Voici la liste des valeurs falsy :
| Type | Valeur(s) falsy |
|---|---|
| Nombres | 0, 0.0, 0j |
| Chaîne | "" (vide) |
| Collection | [], (), {}, set() |
| Spécial | None, False |
Tout le reste est truthy.
for valeur in [0, "", [], {}, None, False, 1, "a", [0], "False"]:
print(f"{str(valeur):<12} → {bool(valeur)}")0 → False
→ False
[] → False
{} → False
None → False
False → False
1 → True
a → True
[0] → True
False → True
Déjà vu. Syntaxe : valeur_si_vrai if condition else valeur_si_faux.
age = 17
statut = "majeur" if age >= 18 else "mineur"
print(statut)mineur
Équivalent à :
if age >= 18:
statut = "majeur"
else:
statut = "mineur"On peut imbriquer des ternaires, mais la lisibilité en souffre :
note = 14
# Imbriqué (lisible ici)
mention = "Bien" if note >= 14 else "Passable" if note >= 10 else "Insuffisant"
print(mention)Bien
Version équivalente avec if/elif — plus lisible pour plus de 2 cas :
note = 14
if note >= 14:
mention = "Bien"
elif note >= 10:
mention = "Passable"
else:
mention = "Insuffisant"
print(mention)Bien
if/elif/else.break et continue (rappel approfondi)Déjà vues en Partie 1. Approfondissement sur des cas subtils.
break dans une boucle imbriquéebreak sort uniquement de la boucle la plus interne.
for i in range(3):
for j in range(3):
if j == 1:
break # sort seulement du for interne
print(f"({i}, {j})")(0, 0)
(1, 0)
(2, 0)
Pour sortir de toutes les boucles, il faut utiliser une variable témoin, une fonction avec return, ou lever une exception (vu en Partie 3).
continue pour filtrer proprement# Afficher les nombres de 1 à 10, sauf les multiples de 3
for n in range(1, 11):
if n % 3 == 0:
continue
print(n, end=" ")1 2 4 5 7 8 10
else sur les bouclesSpécificité Python : for et while peuvent avoir une clause else, qui s’exécute si la boucle se termine normalement (sans break).
nombres = [3, 7, 12, 25, 8, 14]
cible = 25
for n in nombres:
if n == cible:
print("Trouvé !")
break
else:
print("Pas trouvé")Trouvé !
nombres = [3, 7, 12, 25, 8, 14]
cible = 99
for n in nombres:
if n == cible:
print("Trouvé !")
break
else:
print("Pas trouvé")Pas trouvé
n = 17
for i in range(2, n):
if n % i == 0:
print(f"{n} n'est pas premier (divisible par {i})")
break
else:
print(f"{n} est premier")17 est premier
else mal nommé
La clause else des boucles est souvent considérée comme un des choix syntaxiques les plus malheureux de Python : son nom suggère « sinon », mais son sens est « si la boucle s’est terminée sans break ». Mieux vaudrait la lire comme no_break:.
Le TOSA teste son existence et son comportement. En pratique, beaucoup de développeurs Python l’évitent au profit de fonctions avec return plus explicites.
Petit rappel utile pour le TOSA : les versions de Python apportent régulièrement de nouvelles fonctionnalités. Les plus importantes pour le niveau Opérationnel :
| Version | Nouveautés clés |
|---|---|
| Python 3.6 (2016) | f-strings |
| Python 3.7 (2018) | dict conserve l’ordre d’insertion |
| Python 3.8 (2019) | opérateur walrus :=, f"{x=}" |
| Python 3.10 (2021) | match/case, int \| str, zip(strict=True) |
| Python 3.12 (2023) | messages d’erreur encore plus précis |
Le TOSA utilise Python 3.10 — c’est la version de référence du référentiel Isograd 2024-2025.
Pour connaître votre version :
import sys
print(sys.version)Quand on combine plusieurs opérateurs sans parenthèses, Python suit un ordre strict. Voici les règles les plus importantes du plus prioritaire au moins prioritaire :
| Niveau | Opérateurs |
|---|---|
| 1 | ** (puissance) |
| 2 | +x, -x (unaires), ~x |
| 3 | *, /, //, % |
| 4 | +, - (binaires) |
| 5 | Comparaisons : <, <=, >, >=, ==, !=, in, is |
| 6 | not |
| 7 | and |
| 8 | or |
1. Les comparaisons sont prioritaires sur les booléens.
x, y = 5, 10
# age >= 18 and age < 65 se lit (age >= 18) and (age < 65), OK
print(x > 0 and y > 0) # True and True = TrueTrue
2. not est prioritaire sur and et or.
a = False
b = True
print(not a and b) # (not a) and b = True and True = TrueTrue
3. and est prioritaire sur or.
# True or False and False se lit True or (False and False) = True or False = True
print(True or False and False)True
Elles ne coûtent rien et rendent le code lisible pour vous et vos relecteurs :
# ❌ Lecture difficile
if a and b or c and not d:
...
# ✅ Intention claire
if (a and b) or (c and not d):
...Que renvoie 0 or "défaut" ?
0True"défaut"c) "défaut" — or renvoie le premier opérande truthy (ou le dernier si tous falsy). Ici 0 est falsy, donc on passe au suivant.
print(0 or "défaut")défaut
Que renvoie 3 and 5 ?
True358c) 5 — and renvoie le premier opérande falsy (ou le dernier si tous truthy). Ici 3 est truthy, on passe au suivant (5) qui est aussi truthy → c’est le dernier, on le renvoie.
print(3 and 5) # 5
print(0 and 5) # 0 (premier falsy)5
0
Que s’affiche-t-il ?
for n in [1, 2, 3]:
if n == 10:
break
else:
print("terminé")terminébreakb) terminé — la boucle se termine sans rencontrer de break, donc le else s’exécute.
for n in [1, 2, 3]:
if n == 10:
break
else:
print("terminé")terminé
Que renvoie True or False and False ?
TrueFalsea) True — and est prioritaire sur or. L’expression s’évalue comme True or (False and False) = True or False = True.
print(True or False and False)
print(True or (False and False))True
True
Que s’affiche-t-il ?
nom = ""
nom = nom or "inconnu"
print(nom)""inconnuNoneb) inconnu — idiome Python classique : nom or "défaut" renvoie "défaut" si nom est falsy (ici, chaîne vide).
nom = ""
nom = nom or "inconnu"
print(nom)inconnu
Que renvoie ce code ?
def f(x):
print(f"f({x}) appelé")
return x > 0
result = f(5) or f(10)f(5) appelé puis f(10) appelé, résultat Truef(5) appelé seulement, résultat Truef(10) appelé seulement, résultat Trueb) — or est court-circuité : comme f(5) renvoie True, f(10) n’est pas appelé.
def f(x):
print(f"f({x}) appelé")
return x > 0
result = f(5) or f(10)
print("Résultat :", result)f(5) appelé
Résultat : True
Que renvoie not True == False ?
TrueFalsea) True — piège de priorité ! == est plus prioritaire que not. L’expression s’évalue comme not (True == False) = not False = True.
print(not True == False)
print(not (True == False))
print((not True) == False)True
True
True
Les trois valent True ici, mais la première suit la règle not(True==False).
all() à la mainSans utiliser all(), écrivez une fonction tous_positifs(liste) qui renvoie True si tous les éléments de la liste sont > 0, False sinon.
Indice : utilisez break pour sortir dès qu’on trouve un contre-exemple. Bonus : utilisez la clause else de boucle.
def tous_positifs(liste):
...
print(tous_positifs([1, 2, 3, 4]))
print(tous_positifs([1, -2, 3]))
print(tous_positifs([]))def tous_positifs(liste):
for n in liste:
if n <= 0:
return False
return True
print(tous_positifs([1, 2, 3, 4])) # True
print(tous_positifs([1, -2, 3])) # False
print(tous_positifs([])) # True (liste vide)True
False
True
def tous_positifs(liste):
for n in liste:
if n <= 0:
break
else:
return True
return False
print(tous_positifs([1, 2, 3]))
print(tous_positifs([1, -2, 3]))
print(tous_positifs([]))True
False
True
def tous_positifs_natif(liste):
return all(n > 0 for n in liste)
print(tous_positifs_natif([1, 2, 3]))
print(tous_positifs_natif([1, -2, 3]))
print(tous_positifs_natif([]))True
False
True
Écrivez une fonction contient_strict(liste, valeur) qui renvoie True uniquement si la liste contient exactement la même valeur et du même type.
Par exemple : contient_strict([1, 2, 3], 2) → True, mais contient_strict([1, 2, 3], 2.0) → False (bien que 2 == 2.0).
Indice : utilisez à la fois == et type() ou is avec un test explicite.
def contient_strict(liste, valeur):
...
print(contient_strict([1, 2, 3], 2)) # True
print(contient_strict([1, 2, 3], 2.0)) # False
print(contient_strict([1, 2.0, 3], 2.0)) # True
print(contient_strict([1, 2, 3], "2")) # Falsedef contient_strict(liste, valeur):
for x in liste:
if x == valeur and type(x) == type(valeur):
return True
return False
print(contient_strict([1, 2, 3], 2)) # True
print(contient_strict([1, 2, 3], 2.0)) # False
print(contient_strict([1, 2.0, 3], 2.0)) # True
print(contient_strict([1, 2, 3], "2")) # FalseTrue
False
True
False
L’opérateur in utilise ==, donc il trouverait 2.0 dans [1, 2, 3] car 2 == 2.0 est True :
print(2.0 in [1, 2, 3]) # True (via ==)
print(True in [1, 2, 3]) # True aussi ! (True == 1)True
True
La fonction contient_strict ajoute le test de type pour différencier.
Écrivez une fonction acces_autorise(utilisateur, role_requis, actif) qui renvoie True si :
utilisateur n’est pas None.utilisateur a le role_requis.actif est True.Tirez parti du court-circuit de and pour ne pas accéder à utilisateur.get(...) si utilisateur est None.
def acces_autorise(utilisateur, role_requis, actif):
...
# Exemples d'appel
u1 = {"nom": "Alice", "role": "admin"}
u2 = {"nom": "Bob", "role": "user"}
print(acces_autorise(u1, "admin", True))
print(acces_autorise(u2, "admin", True))
print(acces_autorise(None, "admin", True))def acces_autorise(utilisateur, role_requis, actif):
return utilisateur is not None and utilisateur.get("role") == role_requis and actif
u1 = {"nom": "Alice", "role": "admin"}
u2 = {"nom": "Bob", "role": "user"}
print(acces_autorise(u1, "admin", True)) # True
print(acces_autorise(u2, "admin", True)) # False
print(acces_autorise(None, "admin", True)) # False (pas d'erreur !)
print(acces_autorise(u1, "admin", False)) # FalseTrue
False
False
False
Si on avait inversé l’ordre :
def acces_autorise_bugué(utilisateur, role_requis, actif):
return utilisateur.get("role") == role_requis and utilisateur is not None
acces_autorise_bugué(None, "admin", True)--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[33], line 4 1 def acces_autorise_bugué(utilisateur, role_requis, actif): 2 return utilisateur.get("role") == role_requis and utilisateur is not None 3 ----> 4 acces_autorise_bugué(None, "admin", True) Cell In[33], line 2, in acces_autorise_bugué(utilisateur, role_requis, actif) 1 def acces_autorise_bugué(utilisateur, role_requis, actif): ----> 2 return utilisateur.get("role") == role_requis and utilisateur is not None AttributeError: 'NoneType' object has no attribute 'get'
On aurait une AttributeError car on tente d’appeler .get() sur None. L’ordre des conditions dans and compte !
Écrivez une fonction qui renvoie le plus petit élément non nul d’une liste. Si tous sont nuls ou si la liste est vide, renvoyez None.
Contrainte : utilisez la clause else de boucle.
def min_non_nul(liste):
...
print(min_non_nul([0, 3, 0, 7, 2, 0])) # 2
print(min_non_nul([0, 0, 0])) # None
print(min_non_nul([])) # Nonedef min_non_nul(liste):
# Filtrer les non nuls
non_nuls = []
for x in liste:
if x != 0:
non_nuls.append(x)
if non_nuls:
return min(non_nuls)
return None
print(min_non_nul([0, 3, 0, 7, 2, 0]))
print(min_non_nul([0, 0, 0]))
print(min_non_nul([]))2
None
None
def min_non_nul_v2(liste):
non_nuls = [x for x in liste if x != 0]
return min(non_nuls) if non_nuls else None
print(min_non_nul_v2([0, 3, 0, 7, 2, 0]))
print(min_non_nul_v2([0, 0, 0]))
print(min_non_nul_v2([]))2
None
None
La compréhension de liste [x for x in ... if ...] sera vue en Partie 3, mais elle est si fréquente qu’il est utile de la reconnaître dès maintenant.
== vs is : valeur vs identité. Utilisez is avec None, True, False uniquement.and s’arrête au premier falsy, or au premier truthy. Permet d’éviter des erreurs (division par zéro, appel sur None…).A and B et A or B renvoient l’un des opérandes, pas forcément True/False.x = x or defaut pour valeur par défaut (attention aux falsy légitimes comme 0).a if cond else b — OK pour 2 cas, préférez if/elif sinon.else de boucle : s’exécute si la boucle termine sans break. À connaître mais utilisez avec parcimonie.** > unaires > */% > +- > comparaisons > not > and > or. En cas de doute, parenthésez.← Chapitre précédent : Références et mutabilité • Chapitre suivant : Modules →