Partie I - Fondamentaux Python · Chapitre 2

Structures alternatives

3 sections de cours · fiche d'exercices corrigee

1Expressions booleennes et logique

Un programme purement sequentiel execute toujours les memes instructions dans le meme ordre : il calcule, mais ne decide pas. L'instruction conditionnelle introduit le premier mecanisme de controle : choisir une branche selon la valeur de verite d'une expression.

Definition — Expression booleenne

Une expression booleenne est une expression dont l'evaluation produit True ou False. Elle est construite a partir de comparaisons combinees par les connecteurs not, and, or.

Operateurs de comparaison

OperateurSensExemple vrai
==egalite des valeurs3 == 3.0
!=difference"a" != "A"
< <= > >= ordre2 <= 2
inappartenance"y" in "Python"
isidentite des objetsx is None
Ne pas confondre =, == et is
  • = affecte. if x = 3: est une SyntaxError.
  • == compare les valeurs : [1,2] == [1,2] vaut True.
  • is compare les identites (meme objet en memoire) : [1,2] is [1,2] vaut False, ce sont deux listes distinctes.

On reserve is aux singletons : if x is None:, if b is True:. Partout ailleurs, on utilise ==.

Les connecteurs logiques

Tables de verite, avec \(P\) et \(Q\) booleens :

\(P\)\(Q\)not PP and Q P or Q
VVFVV
VFFFV
FVVFV
FFVFF

Le or de Python est le ou inclusif : il est vrai des que l'un des deux operandes l'est. Le ou exclusif s'ecrit P != Q (ou P ^ Q).

Lois de De Morgan

\[ \neg(P \wedge Q) \equiv (\neg P) \vee (\neg Q), \qquad \neg(P \vee Q) \equiv (\neg P) \wedge (\neg Q). \] Elles servent constamment a simplifier une condition negative :

not (a > 0 and b > 0)      # equivaut a
a <= 0 or b <= 0           # forme plus lisible

Priorites

Des plus fortes aux plus faibles : arithmetique > comparaisons > not > and > or. Ainsi

a < b and c > d or e == f
# se lit  ((a < b) and (c > d)) or (e == f)

En cas de doute, parentheser : le correcteur ne penalise jamais des parentheses explicites.

Valeur de verite des objets

Toute valeur peut servir de condition. Sont faux : False, None, 0, 0.0, "", [], (), {}, set(). Tout le reste est vrai.

t = []
if t:                    # equivaut a  if len(t) > 0
    print("liste non vide")
else:
    print("liste vide")   # c'est cette branche
Style

Ecrire if trouve: et non if trouve == True: ; ecrire if not t: et non if len(t) == 0:. Ces formes sont celles attendues et lues instantanement.

2Instructions conditionnelles

Les trois formes

# 1. alternative simple
if condition:
    bloc_si_vrai

# 2. alternative complete
if condition:
    bloc_si_vrai
else:
    bloc_si_faux

# 3. alternative multiple
if condition_1:
    bloc_1
elif condition_2:
    bloc_2
elif condition_3:
    bloc_3
else:
    bloc_par_defaut
Semantique du elif

Les conditions sont evaluees dans l'ordre et la premiere qui est vraie declenche son bloc ; toutes les suivantes sont ignorees, meme si elles sont vraies. Exactement une branche est executee (aucune s'il n'y a pas de else et qu'aucune condition n'est verifiee).

L'ordre des tests compte

def mention(note):
    if note >= 10:
        return "Passable"       # capte TOUT ce qui est >= 10
    elif note >= 12:
        return "Assez bien"     # JAMAIS atteint
    elif note >= 14:
        return "Bien"           # JAMAIS atteint
    return "Insuffisant"

Version correcte : tester du cas le plus restrictif au plus large.

def mention(note):
    """Renvoie la mention associee a une note sur 20."""
    if note < 0 or note > 20:
        return "Note invalide"
    if note >= 16:
        return "Tres bien"
    if note >= 14:
        return "Bien"
    if note >= 12:
        return "Assez bien"
    if note >= 10:
        return "Passable"
    return "Insuffisant"

assert mention(15.5) == "Bien"
assert mention(9.99) == "Insuffisant"
assert mention(20) == "Tres bien"
Methode — elif ou return ?

Dans une fonction, chaque branche se terminant par return, les elif deviennent superflus : la sortie anticipee rend le code plat et lisible. On parle de clauses de garde. C'est la forme a privilegier.

Conditions imbriquees et aplatissement

# version imbriquee : difficile a relire
def acces(age, abonne, invite):
    if age >= 18:
        if abonne:
            return "OK"
        else:
            if invite:
                return "OK"
            else:
                return "Refuse"
    else:
        return "Refuse"

# version aplatie : meme comportement
def acces(age, abonne, invite):
    if age < 18:
        return "Refuse"
    if abonne or invite:
        return "OK"
    return "Refuse"

L'expression conditionnelle

valeur_si_vrai if condition else valeur_si_faux
signe = 1 if x >= 0 else -1
maxi  = a if a > b else b
parite = "pair" if n % 2 == 0 else "impair"

C'est une expression (elle a une valeur), et non une instruction : on peut la placer dans un calcul, un argument, une comprehension de liste. A reserver aux cas courts ; au-dela, un vrai if est plus lisible.

Erreurs frequentes
  • : oublie apres la condition → SyntaxError.
  • else suivi d'une condition : ecrire elif, pas else if.
  • Comparer un flottant a une valeur exacte : if x == 0.1: est presque toujours faux. Ecrire if abs(x - 0.1) < 1e-12:.
  • Tester if 0 < x < 10 est correct en Python, mais if x == 1 or 2: est un piege : 2 est toujours vrai, la condition est donc toujours vraie. Ecrire if x == 1 or x == 2: ou if x in (1, 2):.

3Etudes de cas et table de verite interactive

Etude de cas 1 — position d'un point par rapport a un cercle

from math import sqrt

def position(x, y, r):
    """Position du point (x, y) par rapport au cercle de centre O et rayon r > 0.

    Renvoie 'interieur', 'sur le cercle' ou 'exterieur'.
    """
    d2 = x*x + y*y            # on compare les CARRES : pas de racine, pas d'arrondi
    r2 = r*r
    if d2 < r2:
        return "interieur"
    if d2 == r2:
        return "sur le cercle"
    return "exterieur"

assert position(0, 0, 1) == "interieur"
assert position(3, 4, 5) == "sur le cercle"
assert position(2, 2, 1) == "exterieur"
Astuce

Comparer \(x^2+y^2\) a \(r^2\) plutot que \(\sqrt{x^2+y^2}\) a \(r\) evite un calcul de racine carree (couteux) et une source d'erreur d'arrondi. Sur des entiers, le test devient meme exact. Ce reflexe sert en geometrie algorithmique.

Etude de cas 2 — classification d'un triangle

def triangle(a, b, c):
    """Nature du triangle de cotes a, b, c (reels strictement positifs)."""
    if a <= 0 or b <= 0 or c <= 0:
        return "cotes invalides"
    # inegalite triangulaire : on ordonne pour n'avoir qu'un test
    x, y, z = sorted([a, b, c])          # x <= y <= z
    if x + y <= z:
        return "triangle impossible"
    if x == y == z:
        return "equilateral"
    if abs(x*x + y*y - z*z) < 1e-12:
        return "rectangle"
    if x == y or y == z:
        return "isocele"
    return "quelconque"

assert triangle(3, 4, 5) == "rectangle"
assert triangle(2, 2, 2) == "equilateral"
assert triangle(1, 1, 5) == "triangle impossible"

Deux idees reutilisables : trier les donnees pour reduire le nombre de cas (sans tri, l'inegalite triangulaire demanderait trois tests), et valider les entrees en premier pour que le corps de la fonction travaille sur des donnees saines.

Etude de cas 3 — annee bissextile

Regle gregorienne

Une annee est bissextile si elle est divisible par 4, sauf si elle est divisible par 100 sans l'etre par 400. Ainsi 2024 et 2000 sont bissextiles, 1900 ne l'est pas.

def bissextile(a):
    """Teste si l'annee a est bissextile (calendrier gregorien)."""
    return a % 4 == 0 and (a % 100 != 0 or a % 400 == 0)

for a, attendu in [(2024, True), (2000, True), (1900, False), (2023, False)]:
    assert bissextile(a) == attendu

Noter qu'une fonction booleenne s'ecrit en renvoyant directement l'expression, jamais sous la forme if cond: return True else: return False.

Table de verite interactive

Saisir une expression booleenne en \(P\) et \(Q\) (operateurs and, or, not, ==, !=) et construire sa table ligne par ligne.

Cliquer sur « Construire ».

L'essentiel du chapitre

  • == compare des valeurs, is des objets, = affecte.
  • Dans une cascade if / elif / else, la premiere condition vraie gagne : l'ordre des tests fait partie de l'algorithme.
  • Une fonction booleenne renvoie directement son expression, sans if ... return True.
  • and et or sont court-circuitants : on place a gauche la condition qui protege la droite.
  • Les lois de De Morgan permettent d'eliminer un not englobant.
  • Valider les entrees en premier, trier les donnees pour reduire le nombre de cas.
Fiche d'exercices — chapitre 2Enonces gradues, du calcul d'application au niveau concours, avec corriges detailles.
Ouvrir la fiche →