Vue d'ensemble
Python 3.4, publié le 16 mars 2014, est une version charnière qui pose les fondations de la programmation asynchrone en Python. Le module asyncio fait son entrée dans la bibliothèque standard, ouvrant la voie à un écosystème complet de frameworks asynchrones.
Cette version apporte également enum pour définir des énumérations propres, pathlib pour manipuler les chemins de fichiers de manière orientée objet, le module statistics, et surtout l'intégration de pip via ensurepip.
Nouveautés majeures
asyncio (PEP 3156)
Le module asyncio fournit une boucle événementielle et des coroutines pour écrire du code concurrent mono-thread. Dans Python 3.4, les coroutines sont définies avec le décorateur @asyncio.coroutine et le mot-clé yield from. Cette approche sera simplifiée dans Python 3.5 avec async/await.
import asyncio
# Coroutine qui simule la récupération de données
@asyncio.coroutine
def recuperer_donnees(source, delai):
"""Simule une requête réseau avec un délai variable."""
print(f"Début de la requête vers {source}...")
yield from asyncio.sleep(delai)
donnees = {"source": source, "resultats": delai * 100}
print(f"Réponse reçue de {source}.")
return donnees
# Coroutine qui orchestre plusieurs requêtes en parallèle
@asyncio.coroutine
def agreger_resultats():
"""Lance plusieurs requêtes simultanément et agrège les résultats."""
taches = [
recuperer_donnees("api_utilisateurs", 2),
recuperer_donnees("api_produits", 1),
recuperer_donnees("api_commandes", 3),
]
# gather lance toutes les tâches en parallèle
resultats = yield from asyncio.gather(*taches)
total = sum(r["resultats"] for r in resultats)
print(f"Total agrégé : {total}")
return resultats
# Exécution de la boucle événementielle
boucle = asyncio.get_event_loop()
resultats = boucle.run_until_complete(agreger_resultats())
boucle.close()
# Les 3 requêtes s'exécutent en ~3s au lieu de 6s en séquentiel
for r in resultats:
print(f" {r['source']} -> {r['resultats']} enregistrements")
enum (PEP 435)
Le module enum permet de définir des types énumérés, c'est-à-dire des ensembles de constantes nommées. IntEnum crée des énumérations compatibles avec les entiers, ce qui est utile pour interagir avec du code existant ou des protocoles numériques.
from enum import Enum, IntEnum
# Machine à états pour un système de commande
class EtatCommande(Enum):
BROUILLON = "brouillon"
CONFIRMEE = "confirmee"
EN_PREPARATION = "en_preparation"
EXPEDIEE = "expediee"
LIVREE = "livree"
ANNULEE = "annulee"
# Transitions autorisées entre états
TRANSITIONS = {
EtatCommande.BROUILLON: [EtatCommande.CONFIRMEE, EtatCommande.ANNULEE],
EtatCommande.CONFIRMEE: [EtatCommande.EN_PREPARATION, EtatCommande.ANNULEE],
EtatCommande.EN_PREPARATION: [EtatCommande.EXPEDIEE],
EtatCommande.EXPEDIEE: [EtatCommande.LIVREE],
EtatCommande.LIVREE: [],
EtatCommande.ANNULEE: [],
}
class Commande:
def __init__(self, numero):
self.numero = numero
self.etat = EtatCommande.BROUILLON
def transiter(self, nouvel_etat):
"""Change l'état de la commande si la transition est valide."""
if nouvel_etat in TRANSITIONS[self.etat]:
ancien = self.etat
self.etat = nouvel_etat
print(f"Commande {self.numero} : {ancien.value} -> {nouvel_etat.value}")
else:
print(f"Transition interdite : {self.etat.value} -> {nouvel_etat.value}")
cmd = Commande("CMD-2014-001")
cmd.transiter(EtatCommande.CONFIRMEE) # OK
cmd.transiter(EtatCommande.EN_PREPARATION) # OK
cmd.transiter(EtatCommande.ANNULEE) # Transition interdite
# IntEnum pour les codes HTTP
class CodeHTTP(IntEnum):
OK = 200
CREE = 201
NON_TROUVE = 404
ERREUR_SERVEUR = 500
# Utilisable directement comme entier
print(CodeHTTP.NON_TROUVE == 404) # True
print(CodeHTTP.OK + 1) # 201
pathlib (PEP 428)
Le module pathlib propose une approche orientée objet pour manipuler les chemins de fichiers. L'opérateur / permet de combiner des chemins de manière intuitive, remplaçant avantageusement les appels à os.path.join().
from pathlib import Path
# Comparaison avec os.path
import os.path
# Ancienne manière (os.path)
chemin_ancien = os.path.join(os.path.expanduser("~"), "projets", "mon_app")
existe = os.path.isdir(chemin_ancien)
# Nouvelle manière (pathlib)
chemin = Path.home() / "projets" / "mon_app"
existe = chemin.is_dir()
# Navigation dans l'arborescence d'un projet
racine = Path("/var/www/mon_site")
config = racine / "config" / "settings.toml"
logs = racine / "logs"
print(config.name) # settings.toml
print(config.stem) # settings
print(config.suffix) # .toml
print(config.parent) # /var/www/mon_site/config
# Lister tous les fichiers Python d'un projet
projet = Path(".") # répertoire courant
fichiers_py = sorted(projet.rglob("*.py"))
for f in fichiers_py[:5]:
taille = f.stat().st_size
print(f" {f} ({taille} octets)")
# Lecture et écriture simplifiées
fichier_sortie = Path("resultat.txt")
fichier_sortie.write_text("Traitement terminé avec succès.\n", encoding="utf-8")
contenu = fichier_sortie.read_text(encoding="utf-8")
print(contenu) # Traitement terminé avec succès.
# Création de répertoires imbriqués
dossier = Path("sortie") / "rapports" / "2024"
dossier.mkdir(parents=True, exist_ok=True)
Module statistics
Le nouveau module statistics fournit des fonctions pour calculer des statistiques descriptives de base : moyenne, médiane, écart-type, variance, etc. C'est un ajout bienvenu pour les analyses rapides sans dépendance externe.
import statistics
# Analyse des temps de réponse d'une API (en millisecondes)
temps_reponse = [45, 52, 48, 120, 47, 51, 49, 200, 46, 53, 50, 48]
print(f"Moyenne : {statistics.mean(temps_reponse):.1f} ms")
print(f"Médiane : {statistics.median(temps_reponse):.1f} ms")
print(f"Écart-type : {statistics.stdev(temps_reponse):.1f} ms")
print(f"Variance : {statistics.variance(temps_reponse):.1f}")
# La médiane est plus résistante aux valeurs aberrantes
# que la moyenne (120 ms et 200 ms tirent la moyenne vers le haut)
# Analyse de la distribution des notes d'un examen
notes = [8, 10, 10, 11, 12, 12, 12, 13, 14, 14, 15, 16, 18]
print(f"\nNotes de l'examen :")
print(f"Moyenne : {statistics.mean(notes):.1f}")
print(f"Médiane : {statistics.median(notes):.1f}")
print(f"Mode : {statistics.mode(notes)}")
print(f"Basse méd. : {statistics.median_low(notes)}")
print(f"Haute méd. : {statistics.median_high(notes)}")
pip intégré (ensurepip)
Le module ensurepip garantit que pip est disponible dans chaque installation de Python 3.4+. Auparavant, il fallait télécharger et exécuter manuellement un script d'installation. Ce changement simplifie considérablement le déploiement et la prise en main de Python.
# Vérifier que pip est disponible
# python -m ensurepip --upgrade
# Utilisation standard de pip
# python -m pip install requests
# python -m pip install --upgrade pip
# python -m pip list
# Script de déploiement qui s'assure que pip est présent
import subprocess
import sys
def assurer_pip():
"""Vérifie et installe pip si nécessaire."""
try:
import pip
print(f"pip {pip.__version__} est déjà installé.")
except ImportError:
print("pip absent, installation via ensurepip...")
import ensurepip
ensurepip.bootstrap(upgrade=True)
print("pip installé avec succès.")
def installer_dependances(paquets):
"""Installe une liste de paquets via pip."""
for paquet in paquets:
subprocess.check_call([
sys.executable, "-m", "pip", "install",
"--quiet", paquet
])
print(f" {paquet} installé.")
assurer_pip()
# installer_dependances(["requests", "click", "rich"])
Améliorations mineures
- Le module
tracemallocpermet de suivre les allocations mémoire de l'interpréteur. functools.singledispatchpermet la surcharge de fonctions basée sur le type du premier argument.- Le module
selectorsfournit une abstraction haut niveau pour le multiplexage d'E/S. hashlibest enrichi de nouvelles fonctions de hachage (BLAKE2, SHA-3).- Le module
disaméliore l'affichage du bytecode désassemblé.
Dépréciations et suppressions
Changements notables :
- Le module
asyncioest marqué comme provisoire (API susceptible de changer). - Le format
impest déprécié au profit deimportlib. - Le module
formatterest déprécié.
