Les nouveautés de la version 3.10 de Python
- 2021-06-08
- Publié par : Christophe DELEUZE
- Catégorie : Python
Le Python nouveau sortira en octobre et se cru se nommera : 3.10. Pour résumer, il ne révolutionnera pas la terre et se situera dans la continuité de la 3.9.
Toutefois, si vous voulez en savoir plus, je vous propose une revue rapide des nouveautés et des changements.
Structural Pattern Matching
Python, malgré toute sa puissance et sa popularité, a longtemps manqué d’une forme de contrôle de flux que l’on peut retrouver facilement dans d’autres langages. Un contrôle de flux consiste à prendre une valeur et à vérifier qu’elle correspond à une des conditions prédéfinies. En C et C++, on utilise des switch/case.
Après l’échec de nombreuses propositions visant à ajouter une syntaxe de type commutateur/cas à Python, une récente proposition du créateur du langage Python (Guido van Rossum) et d’un certain nombre d’autres contributeurs a été acceptée pour Python 3.10 : la correspondance de motifs structurels (Structural Pattern Matching). La correspondance de modèle structurel permet non seulement d’effectuer des correspondances simples de style commutateur/cas, mais prend également en charge un plus large éventail de cas d’utilisation.
Les moyens traditionnels de le faire en Python ne sont pas élégants. L’une consiste à écrire une chaîne d’expressions if/elif/else. L’autre consiste à stocker des valeurs à faire correspondre en tant que clés dans un dictionnaire, puis à utiliser les valeurs pour effectuer une action, par exemple, stocker une fonction en tant que valeur et utiliser la clé ou une autre variable comme entrée. Dans de nombreux cas, cela fonctionne bien, mais peut être lourd à construire et à entretenir. C’est pour cette raison que la nouvelle version de Python introduit deux nouveaux mots clés : match et case.
Imaginons une gestion de codes d’erreurs basée sur des if/elif/else, cela donnerait par exemple :
http_code = "500"
if http_code == "200":
print("OK")
elif http_code == "404":
print("Not Found")
elif http_code == "500":
print("Internal Error")
else:
print("Code not found")
Internal Error
Avec le structural pattern matching, nous pouvons remplacer les if, elif et les else par des match et des case :
http_code = "500"
match http_code:
case "200":
print("OK")
case "404":
print("Not Found")
case "500":
print("Internal Error")
case _:
print("Code not found")
Internal Error
On remarque tout de suite que le code est plus élégant.
La correspondance de modèle structurel
Mais le structural pattern matching ne se limite pas à simplement comparer des valeurs. Nous pouvons aussi nous en servir pour faire de la correspondance de modèle structurel. Cela nous permet d’effectuer la même logique de correspondance de cas, mais en fonction de la correspondance entre la structure de notre objet de comparaison et un modèle donné.
Définissons deux dictionnaires, tous deux avec des structures différentes :
dict_a = {
'id': 1,
'meta': {
'source': 'abc',
'location': 'nord'
}
}
dict_b = {
'id': 2,
'source': 'def',
'location': 'nord'
}
Il ne nous reste plus qu’à définir dans une boucle le match et les case associés à leurs patterns respectifs qui permettront d’extraire les données des dictionnaires.
Place à la magie :
for d in [dict_a, dict_b, "test"]:
match d :
case {'id': ident,
'meta':{'source': source,
'location': loc}}:
print(ident, source, loc)
case {'id': ident,
'source': source,
'location': loc}:
print(ident, source, loc)
case _:
print("no match")
1 abc nord
2 def nord
no match
Vous noterez l’emploi de _ dans le dernier case. Cette expression permet de gérer tous les autres cas au même titre qu’un finally.
Des messages d'erreurs plus explicites
La seconde amélioration notable dans cette release, c’est une nette amélioration des messages d’erreurs.
Voici un florilège de quelque uns que vous avez forcément déjà rencontrés :
invalid syntax et l'oubli de fermeture d'accolades ( {} )
Avant :
File "example.py", line 3
some_other_code = foo()
^
SyntaxError: invalid syntax
Après :
File "example.py", line 1
expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
^
SyntaxError: '{' was never closed
invalid syntax et l'oubli des deux points ( : )
Vous ne trouvez pas que c’est mieux non ?
>>> if rocket.position > event_horizon
File "", line 1
if rocket.position > event_horizon
^
SyntaxError: expected ':'
l'oubli de doubler les = dans une if
>>> if rocket.position = event_horizon:
File "", line 1
if rocket.position = event_horizon:
^
SyntaxError: cannot assign to attribute here. Maybe you meant '==' instead of '='?
Le résumé des améliorations sur les messages d'erreurs
Un gros travaux a été fait pour rendre la majorité des erreurs courantes nettement plus expressives et leur pertinence est très appréciable.
Ne vous ayant donné que quelques exemples à titre informatif, je vous invite à aller voir ce lien avec lequel vous aurez beaucoup plus d’information sur les changements liés aux messages d’erreurs : https://docs.python.org/3.10/whatsnew/3.10.html#better-error-messages.
Multiplier les contextes
Dans les petits trucs qui font plaisir, à partir de la 3.10, python supportera officiellement la définition de plusieurs contextes à l’aide de parenthèses :
with (open("fichier1.txt", "r") as fin,
open("fichier2.txt", "w") as fout):
fout.write(fin.read())
C’est une chose que l’on pouvait évidemment faire dans la version précédente de Python, mais c’était fait de manière nettement moins élégante. Voici un exemple pour vous rafraichir la mémoire :
# Exemples avec une version de Python antérieure à 3.10
# Contextes multiples par imbrication de with
with open("fichier1.txt", "r" as fin :
with open("fichier2.txt", "w") as fout:
fout.write(fin.read())
# Contextes multiples avec contextlib
import contextlib
with contextlib.nested(open("fichier1.txt", "r"),
open("fichier2.txt", "w")) as (fin, fout):
fout.write(fin.read())
PEP 604: Union est simplifié par |
Enfin, toujours dans les petits trucs utiles, cette fois-ci concernant le typage de données, si la valeur retournée par une fonction acceptait 2 types différents, il fallait utiliser Union() pour spécifier les types autorisés :
def square(number: Union[int, float]) -> Union[int, float]:
return number ** 2
Maintenant, vous pourrez l’écrire plus simplement avec | :
def square(number: int | float) -> int | float:
return number ** 2
PEP 613: Le concept de TypeAlias
Cela concernera peu de personnes, mais je vous retransmets l’information liée à la PEP 613. La PEP 484 a introduit le concept d’alias de type, exigeant uniquement qu’ils soient des affectations non annotées de niveau supérieur.
Cette simplicité rendait parfois difficile pour les vérificateurs de type de distinguer les alias de type et les affectations ordinaires, en particulier lorsque des références directes ou des types invalides étaient impliqués.
Avant :
StrCache = 'Cache[str]' # un alias de type
LOG_PREFIX = 'LOG[DEBUG]' # une constante de module
Maintenant, le module de typage a une valeur spéciale TypeAlias qui vous permet de déclarer les alias de type plus explicitement :
from typing import TypeAlias
StrCache: TypeAlias = 'Cache[str]' # un alias de type
LOG_PREFIX = 'LOG[DEBUG]' # une constante de module
Le mot de la fin
Python 3.10 est bien une release qui se veut dans la continuité de la 3.9. Pour de plus amples détails sur tous les fix et les nouveautés, je vous invite à vous reporter à la documentation : Release Note de Python 3.10.
J’ai hâte de voir sur le long terme comment la communauté exploitera le structural pattern matching.
D’ici là, comme d’habitude, n’hésitez pas à partager et à commenter. Vos remarques et vos questions sont les bienvenues.
Il faudrait que tu rajoutes la ligne d’import ‘from typing import TypeAlias’ sur la cellule d’exemple du TypeAlias à la fin pour les gens qui veulent tester en copiant collant n’ai pas d’erreur. Il faudrait aussi que tu développes le “multiple contextes” car s’est difficile de comparer aux anciennes versions du coup. Sinon très bon résumé. C’était très clair. J’ai pu me donner une bonne idée des nouveautés de la mise à jour. Je vais creuser encore un peu car j’imagine que se sont les plus parlantes que tu as montré.
Salut et merci pour ton retour. J’ai rajouté l’import et j’ai étoffé la section : multiplier des contextes, avec un exemple complémentaire pour illustrer le avant / après.
Si tu vois d’autres trucs sur d’autres articles n’hésites pas à m’en faire part 🙂 !