Python 3.15 vient de sortir : imports lazy, frozendict et UTF-8 par défaut
Python 3.15 est disponible depuis ce vendredi, et plusieurs de ses nouveautés touchent directement le code que vous écrivez au quotidien, pas seulement les entrailles de l'interpréteur.
Vos imports s'offrent une petite grasse mat'
C'est la nouveauté phare de cette version : le mot-clé lazy (PEP 810) repousse le chargement d'un module jusqu'à sa première utilisation réelle.
Pour rappel, un import classique oblige Python à trouver le fichier, à le lire, à le compiler en bytecode puis à exécuter son contenu. Sur une application aux dépendances touffues, la facture peut atteindre plusieurs secondes au démarrage, même pour des modules qui ne serviront jamais.
lazy import json
lazy from pathlib import Path
Avec ces deux lignes, json et pathlib ne sont réellement chargés qu'au premier appel. Les CLI et les scripts qui importent la moitié de PyPI pour n'en utiliser qu'une poignée sont les premiers à y gagner.
Fini aussi les contorsions pour esquiver un import coûteux en le planquant dans une fonction ou derrière un if TYPE_CHECKING: : tous les imports peuvent rester en haut du fichier, comme le recommande la PEP 8, le guide de style officiel de Python.
Pour du code existant, l'option -X lazy_imports=all (ou la variable d'environnement PYTHON_LAZY_IMPORTS=all) rend tous les imports lazy sans toucher une seule ligne.
Astral, l'éditeur de uv et Ruff dont OpenAI a annoncé le rachat en mars, s'en sert déjà dans une option expérimentale de uv qui accélère jusqu'à 18% la construction des paquets, selon ses propres mesures.
Attention quand même : si un module déclaré en lazy n'arrive pas à se charger, l'exception tombe à sa première utilisation, et non plus au démarrage. Et lazy n'est accepté qu'au niveau du module : dans une fonction, une classe ou un bloc try, c'est la SyntaxError assurée.
frozendict, le dictionnaire immuable
Autre arrivée attendue de longue date, frozendict (PEP 814) rejoint les types natifs : c'est un dictionnaire immuable. Une fois qu'il est créé, impossible d'y ajouter, d'y modifier ou d'y retirer une clé.
De quoi protéger une configuration ou une table de constantes contre une fonction un peu trop zélée. Il sert aussi de valeur par défaut à un argument sans tomber dans le piège classique du {} partagé entre tous les appels.
Comme frozenset, son cousin pour les ensembles présent depuis Python 2.4, il est hachable tant que ses clés et ses valeurs le sont. Il peut donc servir de clé à un autre dictionnaire ou finir dans un set.
Une première proposition avait été rejetée en 2012, faute de cas d'usage convaincants. Quatorze ans plus tard, la donne a changé : Python propose désormais un mode sans GIL (Global Interpreter Lock). Ce verrou global empêche par défaut plusieurs threads d'exécuter du code Python en même temps.
Ce mode et les sous-interpréteurs (plusieurs interpréteurs isolés qui cohabitent dans un même processus, chacun avec son propre GIL) permettent d'exécuter du code Python sur plusieurs cœurs à la fois. Sans eux, il faut répartir le travail entre plusieurs processus, avec le module multiprocessing par exemple.
Un frozendict, que personne ne peut modifier, se partage alors entre threads sans verrou ni risque de conflit.
Ça ne paie pas de mine
Le changement le plus discret de cette version risque pourtant d'en concerner beaucoup : Python utilise désormais l'UTF-8 par défaut, quel que soit l'environnement du système (PEP 686).
Jusqu'ici, un open("fichier.txt") sans paramètre encoding utilisait l'encodage local de la machine, ce qui pouvait réserver son lot de caractères accentués cassés. Un script qui dépendait de l'ancien comportement peut le retrouver en définissant PYTHONUTF8=0 (ou avec l'option -X utf8=0), et encoding="locale" corrige le problème proprement dans le code.
Les compréhensions de liste acceptent aussi l'opérateur (PEP 798) : [x for x in listes] aplatit une liste de listes, là où il fallait passer par itertools.chain() ou par un double for dont personne ne retient jamais l'ordre.
Enfin, les messages d'erreur pensent désormais aux devs qui jonglent entre plusieurs langages : un [1, 2, 3].push(4) vous souffle d'utiliser .append, 'hello'.toUpperCase() renvoie vers .upper et {}.put("a", 1) vous rappelle qu'en Python, on écrit d[k] = v.
Les devs JavaScript et Java se reconnaîtront. 👀
Le JIT passe la seconde
Le compilateur JIT, qui compile à la volée en code machine les portions les plus sollicitées du programme, a été largement remanié depuis son arrivée dans Python 3.13.
Sur la suite de benchmarks pyperformance, il affiche un gain moyen de 7 à 8% sur Linux x86-64 et de 11 à 12% sur les Mac à puce Apple, par rapport à l'interpréteur sans JIT.
Cette moyenne cache de gros écarts d'un benchmark à l'autre : avec le JIT, certains tournent environ 15% moins vite, d'autres plus de deux fois plus vite. Rappelons qu'il reste officiellement expérimental.
Les utilisateurs de Windows, eux, gagnent en vitesse sans rien avoir à activer : les binaires officiels 64 bits passent à l'interpréteur tail-calling apparu avec Python 3.14, une autre façon d'enchaîner les instructions que le compilateur C optimise mieux. Python annonce 15 à 20% de gain moyen sur pyperformance.
Pour savoir où votre propre code passe son temps, la bibliothèque standard accueille aussi Tachyon, un profileur statistique.
Plutôt que d'instrumenter chaque appel de fonction comme cProfile, quitte à ralentir franchement le programme, il relève la pile d'appels jusqu'à un million de fois par seconde, pour un surcoût quasi nul.
Il peut même s'attacher à un processus en cours sans le redémarrer : de quoi ausculter un service qui rame directement en production.
Le Python nouveau est arrivé
Si vos projets tournent encore sous Python 3.10, c'est le bon moment de prévoir une montée de version : la 3.10 est arrivée au bout de ses cinq ans de support le 1er octobre et ne recevra plus aucun correctif de sécurité.
Et pour découvrir les nouveautés autrement qu'en épluchant les notes de version, Barry Warsaw, contributeur historique de CPython, a codé whatsnewt, un jeu d'aventure textuel qui se lance dans le terminal avec uvx --python 3.15 whatsnewt.

Ses dix-huit énigmes se résolvent toutes en écrivant du vrai code Python 3.15, et le jeu cache des easter eggs.
Venant de l'homme qui a droit à son propre easter egg dans CPython (from __future__ import barry_as_FLUFL), le contraire aurait été étonnant.
À propos de l'auteur
Nicolas Lecointre
Fondateur des Joies du Code, je suis l'actualité et la culture des développeurs depuis 2012. Ancien développeur, je me consacre aujourd'hui entièrement au média.
À lire également
Mistral dégaine Large 4, alias Le Chonk, son modèle à 1 000 milliards de paramètres entraîné en Europe
Le Chat a pris du poids — Après des mois de silence assourdissant sur ses grands modèles, Mistral AI a dévoilé le 6 octobre la preview de Mistral Large 4....
Articles similaires
Des neurones humains cultivés en laboratoire jouent à DOOM (oui, vraiment.)
C++26 est officiellement finalisé : la plus grosse mise à jour du langage depuis C++11
Java 26 : Oracle enterre les applets et embarque JavaScript et Python dans la JVM
Des neurones humains cultivés en laboratoire jouent à DOOM (oui, vraiment.)
Mold, le linker ultra-rapide pour Linux, a été réécrit en Rust
C++26 est officiellement finalisé : la plus grosse mise à jour du langage depuis C++11
Java 26 : Oracle enterre les applets et embarque JavaScript et Python dans la JVM
Plus de contenu
Quand je relis mon code pour la 3ème fois et que je vois que j'ai oublié un return
Quand on s'apprête à livrer le projet et que le client nous demande s'il est possible d'ajouter des fonctionnalités
Quand je réécris en seulement 10 lignes de code la fonction de 150 lignes du binôme
Quand le script d’installation me supprime tout un répertoire
Quand le client m'annonce la veille de la mise en prod que le projet ne correspond pas du tout à ses attentes
Quand un incident de prod survient et que je vais chercher tout le reste de l'équipe qui est en réunion
Quand le commercial propose des idées vides de sens en réunion client
Quand je découvre que le précédent dev versionnait avec des dates dans les noms de fichiers
Quand je relis mon code pour la 3ème fois et que je vois que j'ai oublié un return
Quand on s'apprête à livrer le projet et que le client nous demande s'il est possible d'ajouter des fonctionnalités

