Vue d'ensemble
Python 3.9, publié le 5 octobre 2020, apporte des améliorations très attendues qui simplifient le code au quotidien. Les opérateurs d'union sur les dictionnaires, les type hints utilisant directement les types natifs, le module zoneinfo pour les fuseaux horaires et les méthodes removeprefix/removesuffix sur les chaînes de caractères sont autant de nouveautés qui rendent Python plus expressif et plus pratique.
Cette version marque également l'adoption d'un nouveau cycle de release annuel : désormais, une nouvelle version majeure de Python sort chaque mois d'octobre.
Nouveautés majeures
Opérateurs d'union sur les dictionnaires (PEP 584)
Python 3.9 introduit les opérateurs | et |= pour fusionner des dictionnaires. L'opérateur | crée un nouveau dictionnaire à partir de deux existants, tandis que |= met à jour un dictionnaire en place. En cas de clés identiques, les valeurs du dictionnaire de droite l'emportent.
# Fusion de deux dictionnaires avec |
config_defaut = {"theme": "clair", "langue": "fr", "notifications": True}
config_utilisateur = {"theme": "sombre", "taille_police": 14}
config_finale = config_defaut | config_utilisateur
print(config_finale)
# {'theme': 'sombre', 'langue': 'fr', 'notifications': True, 'taille_police': 14}
# Mise à jour en place avec |=
inventaire = {"pommes": 5, "bananes": 3, "oranges": 8}
livraison = {"pommes": 10, "kiwis": 6}
inventaire |= livraison
print(inventaire)
# {'pommes': 10, 'bananes': 3, 'oranges': 8, 'kiwis': 6}
# Cas pratique : fusion de configurations multi-niveaux
env_defaut = {"debug": False, "port": 8000, "host": "localhost"}
env_dev = {"debug": True, "log_level": "DEBUG"}
env_local = {"port": 9000}
# Chaînage de fusions : chaque couche surcharge la précédente
config = env_defaut | env_dev | env_local
print(config)
# {'debug': True, 'port': 9000, 'host': 'localhost', 'log_level': 'DEBUG'}
Type hints avec les types natifs (PEP 585)
Avant Python 3.9, il fallait importer List, Dict, Tuple, etc. depuis le module typing pour annoter les types génériques. Désormais, on peut utiliser directement list, dict, tuple et les autres types natifs comme annotations génériques. Cela simplifie considérablement les imports et la lisibilité du code.
# Avant Python 3.9 :
# from typing import List, Dict, Tuple, Optional
# def traiter_notes(notes: List[int]) -> Dict[str, float]:
# Python 3.9 : les types natifs suffisent
def calculer_statistiques(notes: list[int]) -> dict[str, float]:
"""Calcule les statistiques d'une liste de notes."""
return {
"moyenne": sum(notes) / len(notes),
"minimum": float(min(notes)),
"maximum": float(max(notes)),
}
def trouver_doublons(elements: list[str]) -> set[str]:
"""Identifie les éléments qui apparaissent plus d'une fois."""
vus = set()
doublons = set()
for element in elements:
if element in vus:
doublons.add(element)
vus.add(element)
return doublons
def indexer_produits(produits: list[tuple[str, float]]) -> dict[str, float]:
"""Transforme une liste de paires en dictionnaire."""
return {nom: prix for nom, prix in produits}
# Utilisation
stats = calculer_statistiques([12, 15, 18, 9, 14])
print(stats) # {'moyenne': 13.6, 'minimum': 9.0, 'maximum': 18.0}
doublons = trouver_doublons(["alice", "bob", "alice", "charlie", "bob"])
print(doublons) # {'alice', 'bob'}
Module zoneinfo (PEP 615)
Le module zoneinfo fournit un accès direct à la base de données IANA des fuseaux horaires, sans avoir besoin d'installer pytz. C'est désormais la méthode recommandée pour manipuler les fuseaux horaires en Python.
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
# Créer des datetime avec fuseau horaire
paris = ZoneInfo("Europe/Paris")
new_york = ZoneInfo("America/New_York")
tokyo = ZoneInfo("Asia/Tokyo")
reunion_paris = datetime(2024, 3, 15, 14, 0, tzinfo=paris)
print(f"Paris : {reunion_paris.strftime('%H:%M %Z')}")
# Paris : 14:00 CET
# Conversion entre fuseaux horaires
reunion_ny = reunion_paris.astimezone(new_york)
reunion_tokyo = reunion_paris.astimezone(tokyo)
print(f"New York : {reunion_ny.strftime('%H:%M %Z')}")
# New York : 08:00 EST
print(f"Tokyo : {reunion_tokyo.strftime('%H:%M %Z')}")
# Tokyo : 22:00 JST
# Planification avec gestion des fuseaux
def planifier_rappel(heure_locale, fuseau, delai_heures):
"""Planifie un rappel dans un fuseau horaire donné."""
maintenant = datetime.now(tz=ZoneInfo(fuseau))
rappel = maintenant + timedelta(hours=delai_heures)
return rappel
rappel = planifier_rappel("14:00", "Europe/Paris", 2)
print(f"Rappel prévu à : {rappel.strftime('%H:%M %Z')}")
str.removeprefix() et str.removesuffix() (PEP 616)
Ces deux nouvelles méthodes permettent de retirer un préfixe ou un suffixe d'une chaîne de manière lisible et sûre. Contrairement à un découpage manuel avec lstrip/rstrip (qui opèrent par caractère), ces méthodes travaillent sur des sous-chaînes entières.
# Nettoyage d'URLs
urls = [
"https://www.exemple.fr/api/v2/utilisateurs",
"https://www.exemple.fr/api/v2/produits",
"https://www.exemple.fr/api/v2/commandes",
]
base = "https://www.exemple.fr/api/v2/"
endpoints = [url.removeprefix(base) for url in urls]
print(endpoints) # ['utilisateurs', 'produits', 'commandes']
# Traitement de noms de fichiers
fichiers = ["rapport_2024.csv", "donnees_2024.csv", "archive_2024.csv.bak"]
# Retirer l'extension .csv
sans_extension = [f.removesuffix(".csv") for f in fichiers]
print(sans_extension)
# ['rapport_2024', 'donnees_2024', 'archive_2024.csv.bak']
# Note : archive_2024.csv.bak n'est pas modifié car il ne finit pas par .csv
# Retirer un préfixe de test dans les noms de méthodes
methodes = ["test_connexion", "test_deconnexion", "helper_creation"]
noms = [m.removeprefix("test_") for m in methodes]
print(noms) # ['connexion', 'deconnexion', 'helper_creation']
Améliorations mineures
- Le nouveau parseur PEG remplace le parseur LL(1) historique (PEP 617), ouvrant la voie à de futures améliorations syntaxiques.
math.gcd()accepte désormais un nombre arbitraire d'arguments.- Le module
graphlibest ajouté avecTopologicalSorterpour le tri topologique. - Les décorateurs acceptent désormais n'importe quelle expression valide (PEP 614).
random.randbytes()est ajouté pour générer des octets aléatoires.
Dépréciations et suppressions
Les changements notables dans cette catégorie :
- Le cycle de release passe à un rythme annuel (PEP 602).
- Plusieurs modules rarement utilisés sont dépréciés et prévus pour suppression dans Python 3.12 (PEP 594) :
aifc,audioop,cgi,imghdr, etc. random.shuffle()ne supporte plus le paramètrerandom.
