Vue d'ensemble
Python 3.12, publié le 2 octobre 2023, est une version majeure qui pose les bases d'un futur sans GIL global. La PEP 684 introduit un GIL par interpréteur, permettant à plusieurs sous-interpréteurs de s'exécuter en vrai parallèle. Les f-strings deviennent plus flexibles avec l'imbrication et les commentaires, la nouvelle syntaxe type simplifie la définition des génériques, et les messages d'erreur sont encore plus précis.
Nouveautés majeures
GIL par interpréteur (PEP 684)
Le GIL (Global Interpreter Lock) empêche historiquement l'exécution simultanée de threads Python sur plusieurs cœurs. La PEP 684 donne à chaque sous-interpréteur son propre GIL, ce qui signifie que des sous-interpréteurs distincts peuvent exécuter du code Python en vrai parallèle. C'est une étape fondamentale vers le retrait complet du GIL prévu dans Python 3.13.
Pour l'instant, l'API C est nécessaire pour créer des sous-interpréteurs avec des GIL séparés. Le module interpreters de la bibliothèque standard arrivera dans une version ultérieure.
# Concept : sous-interpréteurs avec GIL séparés
# (API C sous-jacente, illustration conceptuelle)
# Chaque sous-interpréteur possède :
# - Son propre GIL
# - Ses propres modules importés
# - Son propre état global
# En pratique, pour du calcul parallèle sur plusieurs cœurs,
# on utilise encore multiprocessing ou concurrent.futures
from concurrent.futures import ProcessPoolExecutor
import math
def estimer_pi(nb_echantillons):
"""Estime pi par méthode de Monte Carlo."""
import random
dans_cercle = 0
for _ in range(nb_echantillons):
x = random.random()
y = random.random()
if x * x + y * y <= 1.0:
dans_cercle += 1
return 4.0 * dans_cercle / nb_echantillons
# Calcul parallèle sur 4 processus
with ProcessPoolExecutor(max_workers=4) as executeur:
echantillons_par_worker = 2_500_000
futurs = [
executeur.submit(estimer_pi, echantillons_par_worker)
for _ in range(4)
]
estimations = [f.result() for f in futurs]
pi_estime = sum(estimations) / len(estimations)
print(f"Pi estimé : {pi_estime:.6f}")
print(f"Pi réel : {math.pi:.6f}")
# Pi estimé : 3.141428
# Pi réel : 3.141593
Améliorations des f-strings (PEP 701)
Les f-strings de Python 3.12 peuvent désormais contenir des f-strings imbriquées, s'étendre sur plusieurs lignes et inclure des commentaires. L'ancien parseur imposait de nombreuses restrictions qui sont levées grâce au nouveau parseur PEG introduit en Python 3.9.
# F-strings imbriquées (impossible avant 3.12)
colonnes = ['nom', 'age', 'ville']
largeur = 15
en_tete = f"{'|'.join(f'{col:^{largeur}}' for col in colonnes)}"
print(en_tete)
# nom | age | ville
# F-strings multi-lignes avec commentaires
utilisateur = {'prenom': 'Alice', 'nom': 'Dupont', 'points': 1250}
message = f"Bienvenue {
utilisateur['prenom'] # prénom de l'utilisateur
} {
utilisateur['nom'] # nom de famille
}, vous avez {
utilisateur['points'] # points de fidélité
} points."
print(message)
# Bienvenue Alice Dupont, vous avez 1250 points.
# Réutilisation de guillemets identiques
valeurs = {'cle': 'valeur'}
print(f"Résultat : {valeurs['cle']}")
# Résultat : valeur
Syntaxe de paramètres de type (PEP 695)
La PEP 695 introduit une nouvelle syntaxe pour définir les alias de types et les classes génériques. Le mot-clé type remplace TypeAlias, et les paramètres de type s'expriment entre crochets directement après le nom de la classe ou de la fonction. Le code est plus concis et plus lisible qu'avec les annotations manuelles.
# Ancien style (Python < 3.12)
# from typing import TypeVar, TypeAlias, Generic
# T = TypeVar('T')
# Coordonnees: TypeAlias = tuple[float, float]
# Nouveau style (Python 3.12+)
type Coordonnees = tuple[float, float]
type Matrice[T] = list[list[T]]
# Classe générique avec la nouvelle syntaxe
class Pile[T]:
"""Pile générique typée."""
def __init__(self) -> None:
self._elements: list[T] = []
def empiler(self, element: T) -> None:
self._elements.append(element)
def depiler(self) -> T:
if not self._elements:
raise IndexError('La pile est vide')
return self._elements.pop()
def est_vide(self) -> bool:
return len(self._elements) == 0
def __len__(self) -> int:
return len(self._elements)
# Utilisation
pile_entiers = Pile[int]()
pile_entiers.empiler(10)
pile_entiers.empiler(20)
print(pile_entiers.depiler()) # 20
pile_textes = Pile[str]()
pile_textes.empiler('bonjour')
print(len(pile_textes)) # 1
# Fonction générique
def premier[T](sequence: list[T]) -> T:
"""Renvoie le premier élément d'une séquence."""
return sequence[0]
print(premier([3, 1, 4])) # 3
print(premier(['a', 'b', 'c'])) # a
Messages d'erreur améliorés
Python 3.12 continue d'améliorer les messages d'erreur avec des suggestions encore plus pertinentes. Les erreurs d'import, d'attribut et de syntaxe proposent des corrections lorsque le nom est proche d'un identifiant valide. Le message indique également les modules standards susceptibles de fournir le nom cherché.
# Suggestion pour une faute de frappe sur un attribut
# import math
# math.squart(16)
#
# AttributeError: module 'math' has no attribute 'squart'.
# Did you mean: 'sqrt'?
# Suggestion pour un import manquant
# from collections import ordereddict
#
# ImportError: cannot import name 'ordereddict' from 'collections'.
# Did you mean: 'OrderedDict'?
# Suggestion pour une variable locale mal orthographiée
# compteur = 0
# comteur += 1
#
# NameError: name 'comteur' is not defined.
# Did you mean: 'compteur'?
# Suggestion pour un mot-clé mal orthographié
# import = 42
#
# SyntaxError: invalid syntax
# Perhaps you meant 'import' as a keyword?
Améliorations mineures
- Le module
pathlibgagnePath.walk(), une alternative àos.walk(). - Le module
itertoolsajouteitertools.batched()pour découper un itérable en lots de taille fixe. - Le démarrage de l'interpréteur est plus rapide grâce aux optimisations du chargement des modules.
- Les compilés (
.pyc) utilisent un nouveau format avec invalidation par hachage. - Le module
typinggagneoverride(PEP 698) pour marquer les méthodes surchargeant une méthode parente.
Dépréciations et suppressions
Les changements notables dans cette catégorie :
- Le module
distutilsest définitivement supprimé (PEP 632) ; utilisersetuptoolsousysconfigà la place. - Les modules
asynchat,asyncoreetimpsont supprimés. - Les fonctions
unittestdépréciées depuis longtemps sont retirées. - Le module
wtoolsest supprimé.
