Aller au contenu

L'instruction match-case

Le problème : trop de répétition dans l’usage des instructions elif

Section intitulée « Le problème : trop de répétition dans l’usage des instructions elif »

Supposons un code comme celui-ci qui affiche le jour de la semaine correspondant à un chiffre donné

jour = input("Numéro du jour (1-7) : ")
if jour == "1":
nom = "Lundi"
elif jour == "2":
nom = "Mardi"
elif jour == "3":
nom = "Mercredi"
elif jour == "4":
nom = "Jeudi"
elif jour == "5":
nom = "Vendredi"
elif jour == "6":
nom = "Samedi"
elif jour == "7":
nom = "Dimanche"
else:
nom = "Invalide"
print(nom)

Ça fonctionne, mais c’est répétitif. Chaque elif teste la même variable (jour) avec ==. Quand on a beaucoup de cas distincts à traiter, cette approche devient lourd à lire et à maintenir.

La plupart des langages de programmation offrent une structure dédiée pour ce genre de situation. En C, Java, JavaScript et C#, c’est l’instruction switch-case. En Rust, c’est match. Python, longtemps, n’avait rien d’équivalent — il fallait se contenter de chaînes de if-elif.

Ce n’est plus le cas depuis Python 3.10 (octobre 2021), qui a introduit le match-case.

match jour:
case "lundi":
print("Début de semaine")
case "vendredi":
print("Fin de semaine")
case _:
print("Autre jour")

match variable:
case valeur_1:
instructions_1
case valeur_2:
instructions_2
case valeur_3:
instructions_3
case _:
instructions_par_defaut
  1. Le mot-clé match suivi de la valeur à examiner et de :
  2. Un ou plusieurs blocs case suivis d’un motif (pattern) et de :
  3. Le bloc indenté sous chaque case : les instructions à exécuter si le motif correspond
  4. Le cas _ (underscore) : le cas par défaut, qui attrape tout ce qui n’a pas été capté

Voici le même programme qu’avant, réécrit avec match-case :

jour = input("Numéro du jour (1-7) : ")
match jour:
case "1":
nom = "Lundi"
case "2":
nom = "Mardi"
case "3":
nom = "Mercredi"
case "4":
nom = "Jeudi"
case "5":
nom = "Vendredi"
case "6":
nom = "Samedi"
case "7":
nom = "Dimanche"
case _:
nom = "Invalide"
print(nom)

Le code est plus lisible : on voit immédiatement que chaque case correspond à une valeur possible de jour, sans la répétition de jour == à chaque ligne.


Supposons un programme qui interprète des commandes envoyées à un robot.
Ici, nous voyons comment traiter les divers cas de commande avec l’instruction if-elif-else et l’équivalent avec l’instruction match-case.

Comme tu le constateras, lorsque l’on compare une même variable à plusieurs valeurs fixes, l’instruction match-case est souvent plus lisible et plus adaptée qu’une succession de if-elif.

commande = input("Commande envoyé au robot: ")
if commande == "FW" or commande == "fw":
print("Le robot avance")
elif commande == "BW" or commande == "bw":
print("Le robot recule")
elif commande == "LT" or commande == "lt":
print("Le robot tourne à gauche")
elif commande == "RT" or commande == "rt":
print("Le robot tourne à droite")
elif commande == "STOP" or commande == "stop":
print("Le robot s'arrête")
else:
print("Commande invalide")
Aspectif-elif-elsematch-case
Lisibilité avec beaucoup de casRépétitif (variable == partout)Claire et compacte
Conditions complexes
(intervalles, and, or)
Naturel à écrireMoins naturel (possible via if
dans un case)
Tester plusieurs valeurs
pour un même cas
Utilise orUtilise le symbole |
Cas par défautelsecase _

Avec if-elif, regrouper des valeurs demande des or :

jour = input("Jour : ")
if jour == "samedi" or jour == "dimanche":
print("Fin de semaine")
elif jour == "lundi" or jour == "mardi" or jour == "mercredi" or jour == "jeudi" or jour == "vendredi":
print("Jour de semaine")
else:
print("Jour invalide")

Avec match-case, le symbole | (pipe) signifie « ou » et simplifie grandement l’écriture :

jour = input("Jour : ")
match jour:
case "samedi" | "dimanche":
print("Fin de semaine")
case "lundi" | "mardi" | "mercredi" | "jeudi" | "vendredi":
print("Jour de semaine")
case _:
print("Jour invalide")

Le match-case est particulièrement adapté aux menus interactifs où l’utilisateur choisit parmi des options prédéfinies :

print("=== CONVERTISSEUR ===")
print("1. Kilomètres → Milles")
print("2. Milles → Kilomètres")
print("3. Quitter")
choix = input("Ton choix : ")
match choix:
case "1":
km = float(input("Distance en km : "))
milles = km * 0.621371
print(format(km, ".2f"), "km =", format(milles, ".2f"), "milles")
case "2":
milles = float(input("Distance en milles : "))
km = milles * 1.60934
print(format(milles, ".2f"), "milles =", format(km, ".2f"), "km")
case "3":
print("Au revoir !")
case _:
print("Choix invalide")

Le flux de ce programme est facile à visualiser :

flowchart TD
    A["Afficher le menu"] --> B["Lire le choix"]
    B --> C{"choix ?"}
    C -- "1" --> D["Convertir km → mi"]
    C -- "2" --> E["Convertir mi → km"]
    C -- "3" --> F["Au revoir"]
    C -- autre --> G["Choix invalide"]
    D --> H["Fin"]
    E --> H
    F --> H
    G --> H

Le match-case est idéal quand tu dois associer des codes à des significations :

print("=== CODE HTTP ===")
code = int(input("Code de statut : "))
match code:
case 200:
print("OK — La requête a réussi")
case 301:
print("Redirection permanente")
case 404:
print("Page non trouvée")
case 500:
print("Erreur interne du serveur")
case _:
print("Code", code, "— non documenté")
print("=== NOTATION MUSICALE ===")
note = input("Note (do, ré, mi, ...) : ")
match note:
case "do":
frequence = 261.63
case "":
frequence = 293.66
case "mi":
frequence = 329.63
case "fa":
frequence = 349.23
case "sol":
frequence = 392.00
case "la":
frequence = 440.00
case "si":
frequence = 493.88
case _:
frequence = 0
if frequence > 0:
print("Fréquence :", format(frequence, ".2f"), "Hz")
else:
print("Note inconnue")

flowchart TD
    A{"Quel type<br/>de condition ?"}
    A -- "Comparer UNE variable<br/>à plusieurs valeurs" --> B["<b>match-case</b>"]
    A -- "Tester des intervalles<br/>ou inégalités" --> C["<b>if-elif-else</b>"]
    A -- "Combiner des conditions<br/>avec and / or" --> C
    A -- "Tester des variables<br/>différentes" --> C
SituationStructure recommandée
Menu avec options prédéfiniesmatch-case
Codes ou symboles à interprétermatch-case
Regrouper plusieurs valeurs (|)match-case
Intervalles numériquesif-elif-else
Conditions combinées (and, or)if-elif-else
Tests sur des variables différentesif-elif-else

Le match-case compare les valeurs exactement, y compris le type :

choix = input("Ton choix : ") # choix est un str !
match choix:
case 1: # ❌ Compare un str à un int → ne correspondra jamais
print("Option 1")
case "1": # ✅ Compare un str à un str
print("Option 1")

Piège 2 : les noms seuls sont des captures, pas des comparaisons

Section intitulée « Piège 2 : les noms seuls sont des captures, pas des comparaisons »
QUITTER = "q"
match commande:
case QUITTER: # ⚠️ NE compare PAS à la variable QUITTER !
print("Bye") # Ça CAPTURE la valeur dans une nouvelle variable QUITTER

Un nom seul dans un case est interprété comme une variable de capture : il accepte n’importe quelle valeur et la stocke dans ce nom. Pour comparer à une constante, utilise une valeur littérale :

match commande:
case "q": # ✅ Compare à la chaîne "q"
print("Bye")

Sans case _, si aucun motif ne correspond, rien ne se passe; le programme poursuivra simplement son exécution. Dans l’exemple ci-dessous, si jour == "mardi", il n’y aura aucun affichage et aucune erreur.

match jour:
case "lundi":
print("Début de semaine")
case "vendredi":
print("Presque le weekend !")

Bonne pratique: Ajoute toujours un case _ pour gérer les cas imprévus, même si c’est juste pour afficher un message d’erreur :

match jour:
case "lundi":
print("Début de semaine")
case "vendredi":
print("Presque le weekend !")
case _:
print("Jour non géré :", jour)

En pratique, les deux structures se complètent très bien. On utilise match-case pour aiguiller le flux vers le bon cas, puis if-else à l’intérieur d’un case pour affiner avec des conditions plus complexes.

Un programme de commande au restaurant : le match-case gère la catégorie, le if-else gère la logique propre à chaque catégorie.

print("=== MENU ===")
print("1. Boisson")
print("2. Repas")
print("3. Dessert")
choix = input("Ton choix : ")
match choix:
case "1":
format_boisson = input("Petit ou grand ? ")
if format_boisson == "petit":
prix = 2.50
else:
prix = 4.00
print("Boisson :", format(prix, ".2f"), "$")
case "2":
supplement = input("Avec supplément fromage ? (oui/non) : ")
prix = 12.00
if supplement == "oui":
prix = prix + 2.50
print("Repas :", format(prix, ".2f"), "$")
case "3":
print("Dessert : 6.50 $")
case _:
print("Choix invalide")

Le match-case fait l’aiguillage principal (quelle catégorie ?), et le if-else à l’intérieur gère les détails (quel format ? quel supplément ?).


L’inverse est aussi possible : un if-else pour une décision binaire, puis un match-case pour détailler un des cas.

age = int(input("Ton âge : "))
if age < 18:
print("Accès au parc pour enfants")
else:
print("=== ATTRACTIONS ADULTES ===")
print("1. Montagne russe")
print("2. Grande roue")
print("3. Maison hantée")
attraction = input("Ton choix : ")
match attraction:
case "1":
print("Montagne russe — Prépare-toi !")
case "2":
print("Grande roue — Profite de la vue !")
case "3":
print("Maison hantée — Bonne chance...")
case _:
print("Attraction non reconnue")

Voici un exemple plus étoffé qui montre comment les deux structures coopèrent naturellement. Le match-case dirige vers l’opération choisie, et le if-else gère les cas particuliers (division par zéro, racine d’un nombre négatif, etc.).

import math
print("=== CALCULATRICE SCIENTIFIQUE ===")
print("Opérations : +, -, *, /, puissance, racine")
operation = input("Opération : ")
match operation:
case "+" | "-" | "*" | "/":
a = float(input("Premier nombre : "))
b = float(input("Deuxième nombre : "))
match operation:
case "+":
resultat = a + b
case "-":
resultat = a - b
case "*":
resultat = a * b
case "/":
if b == 0:
print("Erreur : division par zéro")
resultat = None
else:
resultat = a / b
if resultat is not None:
print("Résultat :", format(resultat, ".2f"))
case "puissance":
base = float(input("Base : "))
exposant = float(input("Exposant : "))
resultat = base ** exposant
print("Résultat :", format(resultat, ".2f"))
case "racine":
nombre = float(input("Nombre : "))
if nombre < 0:
print("Erreur : racine d'un nombre négatif")
else:
resultat = math.sqrt(nombre)
print("Résultat :", format(resultat, ".2f"))
case _:
print("Opération non reconnue")

Observe comment les rôles se répartissent :

StructureRôle dans cet exemple
match operation: (premier)Aiguille vers la catégorie d’opération
match operation: (imbriqué)Choisit le bon calcul parmi +, -, *, /
if b == 0Protège contre la division par zéro
if resultat is not NoneN’affiche le résultat que si le calcul a réussi
if nombre < 0Protège contre la racine d’un négatif
flowchart TD
    A["Lire l'opération"] --> B{"match operation"}
    B -- "+ - * /" --> C["Lire a et b"]
    C --> D{"match operation<br/>(imbriqué)"}
    D -- "+" --> E["a + b"]
    D -- "-" --> F["a - b"]
    D -- "*" --> G["a * b"]
    D -- "/" --> H{"b == 0 ?"}
    H -- Oui --> I["Erreur"]
    H -- Non --> J["a / b"]
    B -- "puissance" --> K["Lire base, exposant<br/>Calculer"]
    B -- "racine" --> L{"nombre < 0 ?"}
    L -- Oui --> M["Erreur"]
    L -- Non --> N["math.sqrt"]
    B -- "autre" --> O["Non reconnue"]

Un guard est une condition if ajoutée à un case. Le motif doit correspondre et la condition doit être vraie :

mois = int(input("Entre un mois : "))
jour = int(input("Entre un jour : "))
match mois:
case 1 | 3 | 5 | 7 | 8 | 10 | 12 if 1 <= jour <= 31:
print("Date valide")
case 4 | 6 | 9 | 11 if 1 <= jour <= 30:
print("Date valide")
case 2 if 1 <= jour <= 28:
print("Date valide")
case m if 1 <= m <= 12:
print("Jour invalide")
case _:
print("Mois invalide")
Entre un mois : 5
Entre un jour : 25
Date valide

Ici, on utilise match pour analyser la valeur du mois.
Chaque case regroupe les mois qui ont le même nombre de jours :

  • 1 | 3 | 5 | 7 | 8 | 10 | 12 → mois de 31 jours
  • 4 | 6 | 9 | 11 → mois de 30 jours
  • 2 → février (28 jours ici, sans année bissextile)

Après le motif du case, on ajoute un if. Ce if vérifie que le jour entré est valide pour ce mois.

Par exemple:

case 4 | 6 | 9 | 11 if 1 <= jour <= 30:

Cela signifie :

  • Le mois doit être 4, 6, 9 ou 11
  • ET le jour doit être entre 1 et 30

Si ces deux conditions sont vraies, on affiche "Date valide".

Ensuite:

case m if 1 <= m <= 12:

Ici, m capture le mois si aucun des cas précédents ne correspond. Donc :

  • Le mois est valide (entre 1 et 12)
  • Mais le jour ne correspondait à aucun intervalle valide

On affiche alors "Jour invalide".

Finalement :

case _:

Ce cas attrape tout le reste, donc un mois invalide (comme 0 ou 15).

Ce programme vérifie une date en deux étapes :

  1. Est-ce que le mois est valide ?
  2. Est-ce que le jour est valide pour ce mois ?

Le match-case permet donc de choisir le bon groupe de mois, et le if permet de vérifier que le jour correspond au bon nombre de jours.


MotifCorrespond àExemple
42, "bonjour", TrueValeur exacte (littéral)case 42:
x, nomN’importe quelle valeur (capture)case x:
_N’importe quoi (sans capture)case _:
"a" | "b" | "c"L’une des valeurscase "oui" | "o":
case x if cond:Motif + condition (guard)case n if n > 0:

Certains motifs attendront que nous introduisons les structures avancées comme les listes pour être revisités

MotifCorrespond àExemple
[x, y]Liste de 2 élémentscase [a, b]:
[premier, *reste]Liste d’au moins 1 élémentcase [cmd, *args]:
(x, y, z)Tuple de 3 élémentscase ("cercle", r):
{"clé": valeur}Dict contenant cette clécase {"nom": n}: