Accès rapide :
La vidéo
Introduction à l'utilisation de l'instruction « match / case »
Un petit préambule
Un premier exemple simple
Sélection multiple de valeurs
Utilisation du pattern matching
Correspondances avec des types de données
Correspondances avec des structures (des collections)
Travaux pratiques
Les énoncés
Les corrections
Cette vidéo vous présente la nouvelle instruction Python « match / case » qui arrive avec la nouvelle version de Python 3.10.
L'introduction de l'instruction match
/ case
dans Python 3.10 marque une évolution significative dans le langage, offrant une
alternative puissante et flexible aux traditionnelles structures conditionnelles comme if
/ elif
/ else
.
Cette nouveauté s'inspire des instructions similaires présentes dans d'autres langages, tels que switch
/ case
en
C, C++ ou encore Java, mais avec une approche unique adaptée à la philosophie de Python (notamment au travers du pattern matching, nous allons
y revenir plus tard).
Avant de rentrer dans les détails de la syntaxe de cette instruction, je voulais revenir sur la notion de mot clé contextuel (concept introduit dans Python lors de l'ajout de cette nouvelle instruction ; Python 3.10).
Contrairement à des « mots-clés réservés » comme if
ou for
, les instructions match
et
case
ne sont des mots-clés que dans certains contextes spécifiques. Cette particularité préserve la compatibilité ascendante du
langage, évitant les conflits avec les anciens codes où match
et case
auraient pu être utilisés comme noms de
variables ou de fonctions.
Dans l'exemple ci-dessous, les deux mots clé sont utilisés comme nom de variable et comme nom de fonction. Si vous exécutez ce code, vous vous apercevrez que cela fonctionne très bien : dans ce contexte, ils n'ont donc pas de sémantique particulière.
1 2 3 4 5 6 7 8 9 |
match: bool = True def case(param): print("Dans ce contexte, case est le nom de ma fonction") if match: # On test l'état de la variable booléenne case() # On invoque notre fonction |
La structure match
/ case
en Python est conçue pour faciliter la lecture du code et améliorer sa maintenabilité,
en particulier dans les scénarios où de multiples conditions doivent être évaluées. Considérons un premier exemple ou nous allons obtenir une
valeur aléatoire entière (entre 0 et 5) et afficher cette valeur en toutes lettres.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
from random import randint random_value = randint(0, 5) match random_value: case 0: print("Zéro") case 1: print("Un") case 2: print("Deux") case 3: print("Trois") case 4: print("Quatre") case 5: print("Cinq") |
if
/ elif
/ else
,
vous conviendrez aisément que l'approche match
/ case
est plus élégante.
Parfois vous ne souhaitez pas traiter tous les cas de valeurs qu'une variable pourrait avoir et indiquer que ces valeurs ne sont pas considérées.
Pour répondre à ce besoin, vous pouvez utiliser la branche spéciale case _:
qui permet de considérer tous les autres cas.
Voici une adaptation du programme précédent qui utilise cette possibilité : les valeurs supérieures à 5 seront traitées par cette branche.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
from random import randint random_value = randint(0, 10) # Cette fois-ci, je considère des valeurs entre 0 et 10 !!! match random_value: case 0: print("Zéro") case 1: print("Un") case 2: print("Deux") case 3: print("Trois") case 4: print("Quatre") case 5: print("Cinq") case _: # On gère ici, tous les autres cas. print("Autre valeur") |
L'instruction match
permet aussi de sélectionner plusieurs valeurs dans une même branche case
: cela permet de
réduire la quantité de code à produire dans certaines situation. Voici un exemple.
1 2 3 4 5 6 7 8 9 10 |
from random import randint random_value = randint(0, 10) print("Random value =", random_value) match random_value: case 0 | 1 | 2 | 4 | 8: print("Des valeurs que je veux traiter ici") case _: print("Traitement pour les autres valeurs") |
Bien entendu, l'instruction match
sais gérer d'autres types que des numériques. Voici un autre exemple qui montre comment l'utiliser
pour gérer des chaînes de caractères.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import os print("Welcome to SuperShell V1.0") is_running = True while is_running: cmd = input("Veuillez saisir une commande : ") match cmd: case "exit": is_running = False case "ls" | "dir": files = os.listdir(".") for file in files: print(file) case cmd if cmd[0:3] == "cd ": os.chdir(cmd[3:]) case _: print("Commande", cmd, " inconnue") print("Bye bye") |
if
) dans une clause de sélection case
afin de capturer plusieurs valeurs distinctes. Effectivement, le troisième case
permet de traiter les commandes qui commencent
par le préfixe "cd "
.
L'ajout de l'instruction match / case
à Python 3.10 a introduit une puissante capacité de pattern matching (correspondance de motifs)
dans le langage. Cette fonctionnalité permet d'écrire des structures de contrôle plus expressives et flexibles.
Dorénavant, Python permet de combiner le pattern matching avec des vérifications de types. Par exemple, case str():
ou
case int():
permet de vérifier si la valeur correspond à un type spécifique. Voici un exemple de code simpliste.
1 2 3 4 5 6 7 8 9 10 11 12 |
str_value = input("Veuillez saisir une valeur numérique : ") value = eval(str_value) match value: case int(): print("Valeur entière :", value) case float(): print("Valeur flottante :", value) case complex(): print("Valeur complexe :", value) case _: print("Ce n'est pas une valeur numérique") |
Et voici un exemple d'utilisation de ce petit programme :
Veuillez saisir une valeur numérique : 3 + 2j Valeur complexe : (3+2j)
3
correspond à la partie réelle et 2j
à la partie imaginaire.
Mémorisez bien l'utilisation du caractère j
pour indiquer la valeur de la partie imaginaire.
En complément, voici comment déclarer une variable contenant une telle valeur : value = 3 + 2j
.
Outre des valeurs littérales, vous pouvez aussi faire correspondre des structures de données complexes comme les listes, les tuples, ou les dictionnaires. Par exemple, case [x, y, z]: permet de reconnaître une liste à trois éléments et d'assigner chaque élément de la collection à une variable. Voici un exemple d'utilisation :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
first_dict = { "id": 1, "sub_dict": { "part1": "value1", "part2": "value2" } } second_dict = { "id": 2, "part1": "valeur_1", "part2": "valeur_2" } for dictionary in [first_dict, second_dict, "autre"]: match dictionary: # On sélectionne un dictionnaire avec un "id" et un sous-dictionnaire. case {"id": identifier, "sub_dict": {"part1": part1, "part2": part2}}: print("Premier dictionnaire :", identifier, part1, part2) # On sélectionne un dictionnaire avec les champs "id", "part1" et "part2" case {"id": identifier, "part1": part1, "part2": part2}: print("Second dictonnaire :", identifier, part1, part2) case _: print("no match") |
Et voici les résultats affichés par ce programme :
Premier dictionnaire : 1 value1 value2 Second dictonnaire : 2 valeur_1 valeur_2 no match
Voici un dernier exemple de pattern matching permettant de sélectionner des listes avec des tailles distinctes.
Notez bien que pour les deux premiers case
, seules les listes contenant 10
en première valeurs seront considérées.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
str_value = input("Veuillez saisir une liste de valeurs numériques : ") data = eval(str_value) match data: case [10]: print("La liste ne contient que 10") case [10, a]: print(f"La liste contient 10 et {a}") case [10, a, b]: print(f"La liste contient 10 ainsi que {a} et {b}") case list(): print("Une autre liste") case _: print("Ce n'est pas une liste") |
Et voici les résultats affichés par ce programme :
Veuillez saisir une liste de valeurs numériques : [10, 20, 30] La liste contient 10 ainsi que 20 et 30
Exercice 1 : écrivez une fonction hello
qui prend une chaîne de caractères représentant une partie de la journée
(par exemple, "matin", "après-midi", "soir") et renvoie un message de salutation approprié en utilisant l'instruction match / case
.
Exercice 2 : créez une fonction display_contact
qui prend un tuple représentant un contact sous la forme (nom, email) ou
(nom, numéro) et renvoie un message indiquant le type de contact (email ou numéro de téléphone) en utilisant l'instruction match / case
.
Exercice 2 : l'exercice consiste à écrire une fonction display_product
qui utilise l'instruction
match / case
pour classifier des produits en fonction de leur type et de leur prix.
Les produits seront représentés par des dictionnaires contenant trois clés : type
, name
, et price
.
Les types possibles sont "électronique" et "livre". La fonction doit renvoyer une chaîne de caractères décrivant le produit.
Voici les règles de classification à appliquer :
Comme toujours, essayez de faire ces exercices sans regarder directement la correction ci-dessous. ;-)
Exercice 1 : voici ma proposition de correction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def hello(moment): match moment: case "matin": return "Bonjour !" case "après-midi": return "Bonne après-midi !" case "soir": return "Bonne soirée !" case _: return "Salut" # On teste notre fonction print(hello("matin")) # Bonjour ! print(hello("soir")) # Bonne soirée ! |
Exercice 2 : voici ma proposition de correction.
1 2 3 4 5 6 7 8 9 10 11 12 |
def display_contact(contact): match contact: case (nom, email) if "@" in email: return f"{nom} a pour e-mail : {email}" case (nom, numero): return f"{nom} a pour numéro de téléphone : {numero}" # Alice a pour e-mail : alice@example.com print(display_contact(("Alice", "alice@example.com"))) # Bob a pour numéro de téléphone : 123456789 print(display_contact(("Bob", "123456789"))) |
case
et un cas particulier du second. Du coup, l'ordre des deux blocs case
est important : Python, sélectionne le premier bloc qui « match » (qui correspond). Si vous les permutez, l'affichage sous forme
d'un numéro de téléphone sera systématiquement produit. Je vous invite à faire ce test pour vous en convaincre.
Exercice 3 : voici ma proposition de correction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
def display_product(a_product): name = a_product['name'] match a_product: case {"type": "électronique", "price": prix} if prix > 500: return f"{name} est un équipement électronique coûteux" case {"type": "électronique", "price": _}: return f"{name} est un électronique abordable" case {"type": "livre", "price": prix} if prix < 20: return f"{name} est un livre abordable" case {"type": "livre", "price": _}: return f"{name} est un livre coûteux" case _: return "Type de produit inconnu" product = {"type": "électronique", "name": "Smartphone", "price": 800} # Smartphone est un équipement électronique coûteux print(display_product(product)) product = {"type": "livre", "name": "Python pour les Nuls", "price": 15} # Python pour les Nuls est un livre abordable print(display_product(product)) |
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :