Objectifs d'apprentissage
À la fin de ce cours, l'apprenant peut :
- expliquer le modèle « noms, objets, références » et prévoir l'effet d'une affectation, d'un alias et d'une copie ;
- distinguer objets mutables et immuables, et
==deis; - manipuler les types de base, les chaînes (formatage, découpage) et les quatre conteneurs
list,tuple,dict,set; - écrire des compréhensions et des expressions génératrices lisibles ;
- employer
if,for,while,break/continue/elseetmatch; - définir des fonctions (arguments par défaut, mots-clés,
*args,**kwargs, portée,lambda) ; - lever, attraper et enchaîner des exceptions ; garantir un nettoyage avec
finallyetwith; - lire et écrire des fichiers texte avec un encodage explicite ;
- organiser du code en modules et en packages, avec
if __name__ == "__main__"; - écrire des classes, de l'héritage et des
dataclass; - écrire des itérateurs, des générateurs et un décorateur simple ;
- annoter des fonctions, dire ce que les annotations garantissent (rien à l'exécution) et à quoi sert mypy ;
- écrire des tests
unittest; - repérer les pièges classiques (défaut mutable, copie superficielle, identité) ;
- écrire du code idiomatique suivant PEP 8 et PEP 20.
Prérequis
- Savoir ouvrir Terminal, créer un dossier et lire une commande avant de la valider (cours « Terminal macOS »).
- Avoir lu, ou connaître, le cours « Scripting shell et Python » pour les outils de script (
pathlib,argparse,json,subprocess), l'environnement virtuel et Ruff : ce cours ne les reprend pas. - Python 3 présent sur le poste (
python3) : sa présence et sa version sont non vérifiées. Le cours n'en décrit pas l'installation. - Un dossier de test jetable : tous les fichiers du cours y sont créés, jamais ailleurs.
- Aucun exercice ne demande de droits administrateur ni d'accès au réseau.
Parcours et chapitres
1. Exécuter du Python
Trois façons de lancer du code : l'interpréteur interactif (suite d'instructions saisies une à une), un fichier .py passé à l'interpréteur, ou un module (python3 -m nom). Les exemples du cours sont des fichiers à créer dans le dossier de test, puis lancés avec python3 fichier.py. Les lignes de code Python sont affichées dans des blocs de fichier (bouton Copier) ; les commandes Terminal dans des fenêtres de Terminal.
python3 --versionBloc actif : la touche C copie la commande seule, sans invite ni sortie.
- Contexte : Terminal ;
python3présent (non vérifié sur le poste de Stéphane). - Droits : utilisateur normal.
- Effet : affiche le numéro de version de l'interpréteur ; ne modifie rien. La forme
--versionn'a pas été relue dans les pages consultées. - Vérification : une ligne du type
Python 3.x.ys'affiche ; noter ce numéro, il décide de ce qui est disponible (chapitres 7, 12, 15, 18). - Retour arrière : sans objet.
# bonjour.py
print("Bonjour, Python")python3 bonjour.pyBloc actif : la touche C copie la commande seule, sans invite ni sortie.
- Contexte : Terminal, dossier de test contenant
bonjour.py. - Droits : utilisateur normal ; lecture du fichier.
- Effet : exécute le fichier et affiche
Bonjour, Python; peut créer un dossier__pycache__(chapitre 11). - Vérification : la sortie est
Bonjour, Python. - Retour arrière : supprimer le dossier de test en relisant son nom.
Attention : exécuter un fichier .py exécute tout ce qu'il contient. Ne lancer que du code relu, jamais un script reçu d'une source non fiable (voir le chapitre 10 pour eval, exec et pickle).
2. Le modèle de données : noms, objets, références
C'est le chapitre le plus important : la plupart des pièges des chapitres suivants en découlent.
- Tout est objet. Un objet a une identité (qui ne change jamais, « adresse en mémoire » dans la documentation), un type (immuable lui aussi) et une valeur (qui peut changer si l'objet est mutable).
- Une variable n'est pas une boîte contenant une valeur : c'est un nom qui référence un objet. Plusieurs noms peuvent référencer le même objet (aliasing). La documentation du tutoriel précise que cela a un effet sur les objets mutables (listes, dictionnaires) : la modification est visible par tous les noms.
iscompare les identités ;id()renvoie un entier qui représente l'identité ;==compare les valeurs.- Nombres, chaînes et tuples sont immuables ; listes, dictionnaires et ensembles sont mutables. Un tuple qui contient une liste voit sa « valeur » changer si la liste change.
- Pour les types immuables, l'interpréteur peut réutiliser un objet existant : après
a = 1; b = 1,aetbpeuvent ou non désigner le même objet, selon l'implémentation. Ne jamais comparer des nombres ou des chaînes avecis. Aprèsc = []; d = [], la documentation garantit deux listes distinctes.
# noms.py
a = [1, 2]
b = a
c = list(a)
print(a is b, a == c, a is c)
b.append(3)
print(a, c)True True False[1, 2, 3] [1, 2]- Contexte : Python 3 dans un dossier de test ; sortie calculée à la main, non exécutée.
- Droits : utilisateur normal.
- Effet : aucun fichier créé (hors
__pycache__éventuel) ; ajouter àbmodifie l'objet queadésigne aussi, pas celui dec. - Vérification : les deux lignes ci-dessus ; si
a is best faux, relire l'affectationb = a. - Retour arrière : sans objet.
À retenir : une affectation (b = a) ne copie jamais l'objet : elle ajoute un nom. Pour une copie, voir le chapitre 19. Passer un objet à une fonction passe aussi une référence : la fonction peut modifier un objet mutable reçu (le tutoriel note que passer un objet est peu coûteux et que l'appelant voit les modifications).
CPython (l'implémentation de référence) libère un objet quand plus rien ne le référence (comptage de références, avec détection retardée des cycles). La documentation précise de ne pas compter sur une libération immédiate : fermer les fichiers explicitement ou avec with (chapitre 10).
3. Types de base et opérateurs
| Type | Exemples | À savoir |
|---|---|---|
int | 42, -7, 10**30 | précision illimitée |
float | 3.5, 1e-3 | généralement un double C ; erreurs d'arrondi (tutoriel, chapitre « Floating-Point Arithmetic », non relu) |
complex | 3 + 4j | rare |
bool | True, False | sous-classe de int : True + 1 vaut 2 |
NoneType | None | l'absence de valeur ; se teste avec is None (PEP 8) |
str | "texte" | séquence immuable de points de code Unicode |
Opérateurs usuels (formes courantes, non relues ligne à ligne dans les pages consultées) : + - *, / division vraie (toujours un float), // division entière (arrondi vers le bas), % reste, ** puissance.
# nombres.py
print(7 / 2, 7 // 2, -7 // 2, 7 % 2, 2**10)
print(0.1 + 0.2 == 0.3)
print(True + 1, isinstance(True, int))3.5 3 -4 1 1024False2 True- Contexte : Python 3 ; sortie calculée à la main, non exécutée.
- Droits : utilisateur normal.
- Effet : affiche trois lignes, ne modifie rien.
- Vérification : comparer aux trois lignes.
-7 // 2donne-4(arrondi vers le bas, pas vers zéro). - Retour arrière : sans objet.
Attention : ne jamais comparer deux float avec == quand ils viennent d'un calcul (0.1 + 0.2 == 0.3 est faux). Pour de l'argent, éviter les float ; le module decimal existe (non consulté, hors périmètre de ce cours).
Valeur de vérité. Sont faux : None, False, le zéro de tout type numérique, et les séquences ou collections vides ('', (), [], {}, set(), range(0)). Tout le reste est vrai par défaut. Les opérateurs and et or court-circuitent et renvoient l'un de leurs opérandes : x or y vaut x s'il est vrai, sinon y. Les comparaisons s'enchaînent : a < b == c. L'opérateur := (affectation dans une expression) existe.
4. Chaînes de caractères
Une str est immuable : toute « modification » produit une nouvelle chaîne. On les écrit entre '…', "…" ou """…""" (plusieurs lignes). L'indexation commence à 0 ; un indice négatif compte depuis la fin ; une tranche s[a:b:pas] exclut b.
# chaines.py
nom = "Python"
print(nom[1:4], nom[-1], nom[::2], len(nom))
print("a-b-c".partition("-"))
print(", ".join(["un", "deux", "trois"]))
print(" espaces ".strip().upper())
produit, prix = "cahier", 3.5
print(f"{produit:>10} | {prix:6.2f} | {prix=}")yth n Pto 6('a', '-', 'b-c')un, deux, troisESPACES cahier | 3.50 | prix=3.5- Contexte : Python 3 (le spécificateur
=des f-strings est présenté par le tutoriel ; version d'apparition non relevée, voir « Points non vérifiés »). - Droits : utilisateur normal.
- Effet : affiche cinq lignes, ne modifie rien.
- Vérification : comparer aux lignes ci-dessus (
>10aligne à droite sur 10 caractères,6.2fdonne 6 caractères dont deux décimales). - Retour arrière : sans objet.
Méthodes à connaître : split, join (la chaîne appelée est le séparateur), strip, replace, startswith, find (renvoie -1 si absent) contre index (lève ValueError), partition, casefold (comparaison sans casse, plus complète que lower), format. PEP 8 recommande startswith/endswith plutôt que des tranches.
Formatage : préférer les f-strings (f"{valeur:format}"), conversions !s, !r, !a. str() donne la forme lisible, repr() la forme « officielle » proche d'une expression Python (chapitre 12).
Astuce : pour assembler beaucoup de morceaux, "".join(liste) plutôt que des + répétés (conseil usuel, non relu dans les pages consultées).
5. Listes, tuples, dictionnaires et ensembles
| Type | Mutable | Ordre | Doublons | Syntaxe |
|---|---|---|---|---|
list | oui | oui | oui | [1, 2, 3] |
tuple | non | oui | oui | (1, 2, 3) ou 1, 2, 3 ; un seul élément : (1,) |
dict | oui | ordre d'insertion (garanti depuis 3.7) | clés uniques | {"a": 1} |
set | oui | non | non | {1, 2, 3} ; vide : set() (pas {}) |
frozenset | non | non | non | frozenset([1, 2]) |
- Liste. Méthodes :
append,extend,insert,remove,pop,clear,index,count,sort,reverse,copy. Celles qui modifient la liste renvoientNone:x = liste.sort()metNonedansx.sorted(liste)renvoie une nouvelle liste. Pour une file, utilisercollections.deque(appendetpopleftrapides) plutôt qu'une liste dont on retire le premier élément. - Tuple. Immuable, donc utilisable comme clé de dictionnaire s'il ne contient que des éléments immuables (« hashable » ; règle précise non relue). Sert aux petits enregistrements et au déballage :
x, y = point,premier, *reste = valeurs. - Dictionnaire. Les clés doivent être immuables.
d[cle]lèveKeyErrorsi la clé manque ;d.get(cle, defaut)renvoie le défaut ;d.setdefault,d.update,d.pop,d.items(),d.keys(),d.values(). Fusion :{**a, **b}. Parcourirlist(d)donne les clés dans l'ordre d'insertion. - Ensemble. Test d'appartenance rapide et opérations ensemblistes :
a | bunion,a & bintersection,a - bdifférence,a ^ bdifférence symétrique. - Appartenance :
x in conteneur(liste, tuple, chaîne, dict (les clés), ensemble).
# conteneurs.py
mots = "un deux un trois deux un".split()
compte = {}
for mot in mots:
compte[mot] = compte.get(mot, 0) + 1
print(compte)
print(sorted(set(mots)))
premier, *reste = mots
print(premier, reste)
a, b = {1, 2, 3}, {3, 4}
print(a | b, a & b, a - b){'un': 3, 'deux': 2, 'trois': 1}['deux', 'trois', 'un']un ['deux', 'un', 'trois', 'deux', 'un']{1, 2, 3, 4} {3} {1, 2}- Contexte : Python 3 ; sortie calculée à la main, non exécutée. L'ordre d'affichage d'un
setn'est pas garanti (il l'est ici pour de petits entiers en pratique, non garanti par la documentation). - Droits : utilisateur normal.
- Effet : affiche quatre lignes, ne modifie rien.
- Vérification : comparer aux lignes ; si l'ordre des éléments d'un ensemble diffère, c'est normal.
- Retour arrière : sans objet.
Parcours utiles : for i, v in enumerate(seq), for a, b in zip(s1, s2), for k, v in d.items(), reversed(seq), sorted(set(seq)).
Attention : modifier une liste ou un dictionnaire pendant qu'on le parcourt donne des résultats surprenants (comportement connu, non relu dans les pages consultées) : parcourir une copie ou construire une nouvelle liste.
6. Compréhensions et expressions génératrices
Une compréhension construit une liste, un dictionnaire ou un ensemble en une expression.
# comprehensions.py
STOCK = [
{"nom": "cahier", "categorie": "papeterie", "prix": 3.5, "quantite": 10},
{"nom": "stylo", "categorie": "papeterie", "prix": 1.2, "quantite": 0},
{"nom": "lampe", "categorie": "maison", "prix": 18.0, "quantite": 2},
{"nom": "mug", "categorie": "maison", "prix": 6.5, "quantite": 4},
]
carres = [n * n for n in range(6) if n % 2 == 0]
en_stock = [a["nom"] for a in STOCK if a["quantite"] > 0]
prix_par_nom = {a["nom"]: a["prix"] for a in STOCK}
categories = {a["categorie"] for a in STOCK}
valeur = sum(a["prix"] * a["quantite"] for a in STOCK)
print(carres, en_stock, prix_par_nom["mug"], sorted(categories), valeur)[0, 4, 16] ['cahier', 'lampe', 'mug'] 6.5 ['maison', 'papeterie'] 97.0- Contexte : Python 3 ; données fictives du cours ; sortie calculée à la main (3,5 × 10 + 0 + 18 × 2 + 6,5 × 4 = 35 + 0 + 36 + 26 = 97), non exécutée.
- Droits : utilisateur normal.
- Effet : affiche une ligne, ne modifie rien.
- Vérification : la ligne ci-dessus (un arrondi de virgule flottante peut faire apparaître
97.00000000000001ailleurs : voir le chapitre 3). - Retour arrière : sans objet.
La dernière forme, sum(x for x in …), est une expression génératrice : elle ne construit pas de liste intermédiaire, elle produit les valeurs une à une (chapitre 13). Entre parenthèses de l'appel, pas besoin de doubler les parenthèses quand elle est l'unique argument.
À retenir : une compréhension sert à transformer ou filtrer. Si elle déborde d'une ligne ou comporte plus d'une condition imbriquée, une boucle for est plus lisible (PEP 20 : « Flat is better than nested », « Readability counts »).
7. Contrôle de flux
# flux.py
def classer(code):
match code:
case 200:
return "ok"
case 404:
return "introuvable"
case _:
return "autre"
for n in range(2, 8):
for diviseur in range(2, n):
if n % diviseur == 0:
break
else:
print(n, "est premier")
print(classer(404), classer(500))2 est premier3 est premier5 est premier7 est premierintrouvable autre- Contexte : Python 3.10 ou plus pour
match(instruction de filtrage par motif, documentée comme apparue en 3.10) ; version de Stéphane non vérifiée. Sortie calculée à la main. - Droits : utilisateur normal.
- Effet : affiche six lignes, ne modifie rien.
- Vérification : comparer aux lignes ci-dessus. Sur une version antérieure à 3.10,
matchest une erreur de syntaxe : remplacer parif/elif. - Retour arrière : sans objet.
if/elif/else; la condition n'a pas besoin de parenthèses ; l'indentation (4 espaces, PEP 8) délimite les blocs.for x in itérable;range(a, b, pas)exclutb;while condition.breaksort de la boucle la plus interne ;continuepasse à l'itération suivante ; la clauseelsed'une boucle s'exécute si la boucle va jusqu'au bout sansbreak(c'est le cas de l'exemple des nombres premiers).matchaccepte aussi des motifs de tuple (case (0, y):) ; un tiret bas_est le cas par défaut.passne fait rien (bloc vide à remplir plus tard).
Astuce : en Python, on parcourt directement les éléments (for a in STOCK) ; on évite for i in range(len(x)) quand l'indice ne sert pas ; si l'indice sert, enumerate.
8. Fonctions
# fonctions.py
def decrire(nom, prix, /, devise="EUR", *, majuscules=False):
"""Retourne une description courte (docstring)."""
texte = f"{nom} : {prix:.2f} {devise}"
return texte.upper() if majuscules else texte
def somme(*valeurs, **options):
return sum(valeurs), sorted(options)
def compteur():
n = 0
def suivant():
nonlocal n
n += 1
return n
return suivant
print(decrire("cahier", 3.5))
print(decrire("mug", 6.5, devise="USD", majuscules=True))
print(somme(1, 2, 3, arrondi=2, verbose=True))
c = compteur()
print(c(), c(), c())
paires = [(1, "un"), (3, "trois"), (2, "deux")]
print(sorted(paires, key=lambda p: p[1]))cahier : 3.50 EURMUG : 6.50 USD(6, ['arrondi', 'verbose'])1 2 3[(2, 'deux'), (3, 'trois'), (1, 'un')]- Contexte : le
/(paramètres « positionnels seulement ») est présenté par le tutoriel sans date d'apparition ; de mémoire 3.8, non confirmé ; version de Stéphane non vérifiée. Sortie calculée à la main. - Droits : utilisateur normal.
- Effet : affiche cinq lignes, ne modifie rien.
- Vérification : comparer aux lignes ;
decrire(prix=3.5, nom="x")est uneTypeError, carnometprixsont positionnels seulement. - Retour arrière : sans objet.
Points clés :
- Arguments. Positionnels, par mot-clé (
devise="USD"), valeurs par défaut. Dans la signature,/termine les paramètres positionnels seulement,*commence les paramètres par mot-clé seulement.*valeurscollecte les arguments positionnels en tuple,**optionsles arguments nommés en dictionnaire. À l'appel,f(*liste)etf(**dico)déballent. - Valeur par défaut évaluée une seule fois : voir le piège du chapitre 19.
- Retour. Sans
return, une fonction renvoieNone. Plusieurs valeurs : un tuple, que l'on déballe. - Portée (LEGB). Un nom est cherché dans le Local, puis les fonctions englobantes (Enclosing), puis le Global du module, puis les Built-ins. Une affectation crée un nom local ;
nonlocal(fonction englobante) etglobal(module) changent ce comportement. Éviterglobal: passer des arguments, renvoyer des valeurs. lambda: fonction anonyme d'une seule expression, utile pourkey=. PEP 8 déconseille de l'affecter à un nom : écrire undef.- Docstring : première chaîne du corps, accessible par
fonction.__doc__; PEP 8 en recommande une pour les fonctions publiques (rappel, section non relue en détail). - Fonctions de première classe : une fonction est un objet ; on peut la passer en argument, la renvoyer, la stocker.
9. Exceptions
Une erreur de syntaxe empêche le programme de démarrer ; une exception survient à l'exécution et peut être gérée.
# erreurs.py
class StockError(Exception):
"""Erreur propre à l'application (les noms finissent en Error)."""
def lire_entier(texte):
try:
return int(texte)
except ValueError:
return None
def retirer(stock, nom, quantite):
try:
disponible = stock[nom]
except KeyError as erreur:
raise StockError(f"article inconnu : {nom}") from erreur
else:
if quantite > disponible:
raise StockError(f"seulement {disponible} en stock")
stock[nom] = disponible - quantite
finally:
print("fin du traitement")
print(lire_entier("42"), lire_entier("abc"))
stock = {"mug": 4}
retirer(stock, "mug", 1)
try:
retirer(stock, "lampe", 1)
except StockError as erreur:
print("erreur :", erreur, "| cause :", type(erreur.__cause__).__name__)
print(stock)42 Nonefin du traitementfin du traitementerreur : article inconnu : lampe | cause : KeyError{'mug': 3}- Contexte : Python 3 ; données fictives ; sortie calculée à la main, non exécutée.
- Droits : utilisateur normal.
- Effet : affiche cinq lignes ; le dictionnaire
stock(en mémoire seulement) passe de 4 à 3 pourmug. - Vérification : comparer aux lignes ;
finallys'exécute dans les deux appels, y compris celui qui lève l'exception. - Retour arrière : sans objet (aucun fichier).
Règles :
try/except Type as e/else(si aucune exception) /finally(toujours, idéal pour nettoyer).- Attraper des types précis ; PEP 8 déconseille le
except:nu et recommande de limiter letryau strict nécessaire. Unexcept Exceptionlarge doit faireraiseà nouveau (ou journaliser) plutôt qu'avaler l'erreur (PEP 20 : « Errors should never pass silently »). raise X(...)lève ;raiseseul relance l'exception courante ;raise X from causeenchaîne explicitement ;raise X from Nonesupprime l'enchaînement affiché.- Les exceptions d'application dérivent de
Exception. - Groupes d'exceptions et
except*: documentés comme nouveauté de 3.11 (réunir plusieurs erreurs, p. ex. avec des tâches concurrentes).e.add_note("…")ajoute une note à une exception (version d'apparition non relevée). - Noms courants à reconnaître :
ValueError,TypeError,KeyError,IndexError,OSError(dont les erreurs de fichier),ZeroDivisionError.
10. Fichiers, contextes (with) et données non fiables
# fichiers.py
from pathlib import Path
chemin = Path("essai-demo.txt")
with open(chemin, "w", encoding="utf-8") as f:
f.write("ligne 1\nligne 2\n")
with open(chemin, encoding="utf-8") as f:
for numero, ligne in enumerate(f, start=1):
print(numero, ligne.rstrip("\n"))
print(f.closed)
chemin.unlink()1 ligne 12 ligne 2True- Contexte : Python 3, dossier de test jetable.
- Droits : utilisateur normal ; écriture dans le dossier courant.
- Effet : crée
essai-demo.txt, écrase un fichier de ce nom s'il existait (mode"w"), le relit, puis le supprime (unlink). - Vérification : les trois lignes ci-dessus ;
f.closedestTrueaprès le blocwith, etessai-demo.txtn'existe plus. - Retour arrière : si le script s'arrête avant
unlink, supprimeressai-demo.txtà la main en relisant son nom. - Modes :
"r"lecture (défaut),"w"écriture qui écrase,"a"ajout,"r+"lecture/écriture, suffixe"b"pour le binaire (octets, sans traduction de fins de ligne). - En mode texte, les fins de ligne sont converties en
\nà la lecture et reconverties à l'écriture selon la plate-forme. - Toujours préciser
encoding="utf-8"(ou l'encodage voulu) en mode texte : la page consultée indique que l'encodage par défaut est UTF-8 si l'on n'en donne pas, mais l'indiquer rend le script indépendant de la version (ce comportement par défaut dépend de la version : non vérifié pour les versions antérieures). withgarantit la fermeture, même si une exception survient. La documentation du modèle de données ajoute de ne pas compter sur une libération immédiate des objets : fermer explicitement ou parwith.- Parcourir
for ligne in flit ligne à ligne sans tout charger en mémoire ;f.read()charge tout. - Pour
pathlib,json,argparse: cours « Scripting shell et Python ».
Un gestionnaire de contexte est un objet qui définit __enter__() et __exit__() (modèle de données) :
# contexte.py
class Minuteur:
def __enter__(self):
print("début")
return self
def __exit__(self, type_exc, valeur, trace):
print("fin")
return False # ne masque pas l'exception éventuelle
with Minuteur():
print("corps")débutcorpsfin- Contexte : Python 3 ; sortie calculée à la main. Le détail de la valeur de retour de
__exit__(vrai : l'exception est supprimée) est à relire dans la référence ; iciFalseest le choix prudent. - Droits : utilisateur normal.
- Effet : affiche trois lignes, ne modifie rien. Le module
contextlib(écrire un contexte avec un générateur) n'a pas été consulté. - Vérification : les trois lignes.
- Retour arrière : sans objet.
Attention — données non fiables. Une donnée externe (fichier reçu, page web, réponse d'API ou de modèle) est une donnée, jamais une instruction.
- Ne jamais passer une telle donnée à
eval()ni àexec(): ces fonctions exécutent du code (comportement général, non relu dans les pages consultées). - Ne jamais désérialiser avec
pickledes données non fiables : la documentation du tutoriel avertit que le faire « peut exécuter du code arbitraire ». Préférerjson, en validant la structure et en limitant la taille lue. - Ne jamais ouvrir un chemin construit à partir d'une entrée externe sans vérifier qu'il reste dans le dossier attendu.
11. Modules et packages
Un module est un fichier .py ; son nom (sans .py) est l'identifiant à importer. Un package est un dossier de modules contenant un fichier __init__.py.
# outils.py
def doubler(n):
return n * 2
if __name__ == "__main__":
print("exécuté directement :", doubler(21))# principal.py
import outils
from outils import doubler as d
print(outils.doubler(4), d(5))
print(outils.__name__)8 10outilspython3 outils.pypython3 principal.pyBloc actif : la touche C copie la commande seule, sans invite ni sortie.
- Contexte : Terminal, dossier de test contenant
outils.pyetprincipal.py; sortie des deux commandes calculée à la main (exécuté directement : 42, puis les deux lignes ci-dessus). - Droits : utilisateur normal ; création possible du dossier
__pycache__(octets-code mis en cache, qui n'accélèrent que le chargement). - Effet :
python3 outils.pyafficheexécuté directement : 42;python3 principal.pyimporte sans exécuter le bloc protégé, car__name__vaut"outils"à l'import et"__main__"à l'exécution directe. - Vérification : comparer aux sorties ; supprimer
__pycache__est sans danger. - Retour arrière : supprimer le dossier de test en relisant son nom.
- Formes d'import :
import m,from m import a, b,import m as alias. Éviterfrom m import *(pollue l'espace de noms ; ignore les noms commençant par_). - Chemin de recherche (
sys.path) : dossier du script, puisPYTHONPATH, puis les emplacements par défaut de l'installation (dontsite-packages). Attention : un fichier local nommé comme un module standard (par exemplejson.py) peut le masquer (comportement qui découle de l'ordre de recherche ; exemple non relu). - Package :
sound/__init__.py,sound/effects/echo.py;from sound.effects import echo. Imports relatifs :from . import echo,from .. import formats(le module principal exécuté directement ne peut pas en utiliser). __all__dans__init__.pyliste ce qu'exporteimport *.dir(module)liste les noms d'un module.- Ordre des imports (PEP 8) : bibliothèque standard, puis tiers, puis locaux, avec une ligne vide entre groupes.
- Les packages tiers s'installent avec
pipdans un environnement virtuel (chapitre 17).
12. Classes, objets et dataclasses
# classes.py
class Article:
"""Un article du stock (données fictives)."""
def __init__(self, nom, prix, quantite=0):
self.nom = nom
self.prix = prix
self.quantite = quantite # attribut d'instance
def valeur(self):
return self.prix * self.quantite
def __repr__(self):
return f"Article({self.nom!r}, {self.prix}, {self.quantite})"
class Perissable(Article):
def valeur(self):
return super().valeur() * 0.5
a = Article("mug", 6.5, 4)
p = Perissable("yaourt", 1.0, 10)
print(a, a.valeur())
print(p.valeur(), isinstance(p, Article), issubclass(Perissable, Article))Article('mug', 6.5, 4) 26.05.0 True True- Contexte : Python 3 ; sortie calculée à la main (6,5 × 4 = 26 ; 1 × 10 × 0,5 = 5).
- Droits : utilisateur normal.
- Effet : affiche deux lignes, ne modifie rien.
- Vérification : les deux lignes ;
print(a)utilise__repr__faute de__str__. - Retour arrière : sans objet.
À savoir :
selfest l'instance, passée automatiquement :x.f()équivaut àClasse.f(x).- Attribut d'instance (défini dans
__init__) contre attribut de classe (défini dans le corps de la classe, partagé par toutes les instances). Piège documenté : une liste mutable en attribut de classe est partagée (le tutoriel montre deux chiens dont lestricksse mélangent). Créer les listes dans__init__. - Héritage :
class B(A);super()appelle la méthode de la classe parente ;isinstance,issubclass. L'héritage multiple existe, avec un ordre de résolution des méthodes (MRO) ; l'éviter sauf besoin clair. - Pas de vraie confidentialité. Convention PEP 8 :
_nomsignale un détail d'implémentation ;__nomdéclenche une altération du nom (_Classe__nom) pour éviter les collisions avec les sous-classes. - Méthodes spéciales (modèle de données) :
__repr__/__str__(représentations),__eq__et__hash__(comparaison et utilisation comme clé),__len__,__iter__,__getitem__(conteneurs),__enter__/__exit__(contextes).__repr__devrait, si possible, ressembler à l'expression qui recrée l'objet. - Les décorateurs
@property,@classmethod,@staticmethodexistent : non consultés dans ce cours (voir « Points non vérifiés »).
Les dataclass (module dataclasses, depuis 3.7) génèrent __init__, __repr__ et __eq__ à partir des annotations de champs :
# donnees.py
from dataclasses import dataclass, field, replace
@dataclass
class Commande:
reference: str
lignes: list[str] = field(default_factory=list)
@dataclass(frozen=True)
class Point:
x: int
y: int
c = Commande("CMD-1")
c.lignes.append("cahier")
p = Point(1, 2)
q = replace(p, x=5)
print(c, p == Point(1, 2), q)
try:
p.x = 9
except Exception as erreur:
print(type(erreur).__name__)Commande(reference='CMD-1', lignes=['cahier']) True Point(x=5, y=2)FrozenInstanceError- Contexte : Python 3.9 ou plus pour
list[str]en annotation (génériques natifs, d'après la pagetyping) ;dataclassdepuis 3.7 ; sortie calculée à la main. - Droits : utilisateur normal.
- Effet : affiche deux lignes, ne modifie rien.
- Vérification : les deux lignes ; la seconde prouve que
frozen=Trueinterdit l'affectation. - Retour arrière : sans objet.
- Un défaut mutable direct (
lignes: list = []) est refusé pardataclass(ValueError) : utiliserfield(default_factory=list). - Options (versions indiquées par la documentation) :
frozen,order,unsafe_hash(3.7) ;kw_only,slots(3.10) ;weakref_slot(3.11).__post_init__s'exécute après__init__. asdict,astuple,replace,fieldsaident à manipuler les instances.- Quand l'utiliser : un type surtout fait de données (un enregistrement). Pour un comportement riche, une classe ordinaire.
13. Itérateurs et générateurs
Un itérable peut être parcouru par for. Le protocole : __iter__() renvoie un itérateur ; __next__() fournit l'élément suivant et lève StopIteration à la fin. Un générateur est une fonction contenant yield : elle crée un itérateur sans écrire __iter__/__next__.
# generateurs.py
def decompte(n):
while n > 0:
yield n
n -= 1
def lignes_longues(lignes, minimum):
for ligne in lignes:
if len(ligne) >= minimum:
yield ligne
g = decompte(3)
print(next(g), list(g), list(g))
print(sum(i * i for i in range(10)))
print(list(lignes_longues(["a", "abc", "ab", "abcd"], 3)))3 [2, 1] []285['abc', 'abcd']- Contexte : Python 3 ; sortie calculée à la main (
285est la valeur donnée par le tutoriel pour la somme des carrés de 0 à 9). - Droits : utilisateur normal.
- Effet : affiche trois lignes, ne modifie rien.
- Vérification : les trois lignes ;
list(g)une seconde fois renvoie[]car le générateur est épuisé (on ne peut le parcourir qu'une fois : comportement connu, non relu en toutes lettres dans les pages consultées). - Retour arrière : sans objet.
À retenir : un générateur calcule à la demande : utile pour de gros volumes (lecture d'un fichier ligne à ligne) car il ne construit pas toute la liste. Une liste, elle, se relit autant de fois qu'on veut. Outils proches : enumerate, zip, reversed, sorted, range, collections.deque.
14. Décorateurs, en bref
Un décorateur est une fonction qui reçoit une fonction et en renvoie une autre ; @nom au-dessus d'un def est un raccourci pour f = nom(f). Utiliser functools.wraps conserve le nom et la docstring de la fonction décorée.
# decorateurs.py
from functools import wraps
def tracer(fonction):
@wraps(fonction)
def enveloppe(*args, **kwargs):
print(f"appel de {fonction.__name__}")
return fonction(*args, **kwargs)
return enveloppe
@tracer
def addition(a, b):
"""Additionne deux valeurs."""
return a + b
print(addition(2, 3))
print(addition.__name__, addition.__doc__)appel de addition5addition Additionne deux valeurs.- Contexte : Python 3 ; sortie calculée à la main.
- Droits : utilisateur normal.
- Effet : affiche trois lignes, ne modifie rien.
- Vérification : sans
@wraps, la deuxièmeprintafficheraitenveloppe None. - Retour arrière : sans objet.
functools.cache (depuis 3.9) mémorise les résultats d'une fonction sans limite de taille ; functools.lru_cache borne le cache ; functools.partial fige une partie des arguments. Attention : ne mettre en cache que des fonctions pures dont les arguments sont hashables, et surveiller la mémoire (le cache non borné grossit).
15. Typage : annotations et mypy
Les annotations décrivent les types attendus : def f(x: int, y: str = "a") -> bool:, valeur: list[int] = []. Le runtime Python ne les impose pas (la documentation typing le dit explicitement) : elles servent aux vérificateurs de types tiers (mypy, entre autres), aux éditeurs et à la lisibilité.
# typage.py
def moyenne(valeurs: list[float]) -> float:
return sum(valeurs) / len(valeurs)
def premier(valeurs: list[int]) -> int | None:
return valeurs[0] if valeurs else None
print(moyenne([1.0, 2.0, 6.0]), premier([]))
print(moyenne.__annotations__)3.0 None{'valeurs': list[float], 'return': <class 'float'>}- Contexte : Python 3.10 ou plus pour la syntaxe
X | Yd'union (la pagetypingindique 3.10) ;list[float]natif depuis 3.9. Le format exact d'affichage de__annotations__varie selon la version (et selon l'évaluation différée des annotations introduite en 3.14) : non vérifié. - Droits : utilisateur normal.
- Effet : affiche deux lignes, ne modifie rien ;
moyenne(["a"])s'exécuterait quand même jusqu'à l'erreur du calcul : rien n'est vérifié à l'avance. - Vérification : première ligne
3.0 None. - Retour arrière : sans objet.
- Génériques natifs
list[int],dict[str, int],tuple[int, str](3.9+) ; les ancienstyping.List,typing.Dictsont dépréciés au profit de ces formes.Optional[X]équivaut àX | None.Anydésactive la vérification. Aussi :Callable,Protocol(typage structurel),TypedDict. - Alias et génériques à syntaxe dédiée :
type Vecteur = list[float]etdef f[T](x: T) -> T:(depuis 3.12, PEP 695). À partir de 3.14, les annotations sont évaluées de façon différée (PEP 649), ce qui autorise des références avant définition sans guillemets. Selon la version de Stéphane, utiliser les formes anciennes. - mypy est un vérificateur statique tiers :
mypy programme.pylit le code sans l'exécuter et signale les incohérences ; on peut ignorer ses avertissements et exécuter quand même. Par défaut, il ne vérifie pas les fonctions sans annotation ;--strictactive des contrôles supplémentaires. Sa présence sur le poste est non vérifiée ; ce cours ne l'installe pas. Ruff (cours « Scripting shell et Python ») est un autre outil, de lint.
Astuce : annoter d'abord les signatures de fonctions publiques ; c'est là que les annotations rapportent le plus.
16. Tests avec unittest
unittest est dans la bibliothèque standard. Créer deux fichiers dans le dossier de test :
# stock.py
def valeur_totale(articles):
return sum(a["prix"] * a["quantite"] for a in articles)# test_stock.py
import unittest
from stock import valeur_totale
ARTICLES = [
{"nom": "cahier", "prix": 3.5, "quantite": 10},
{"nom": "mug", "prix": 6.5, "quantite": 4},
]
class TestStock(unittest.TestCase):
def test_valeur_totale(self):
self.assertEqual(valeur_totale(ARTICLES), 61.0)
def test_liste_vide(self):
self.assertEqual(valeur_totale([]), 0)
def test_article_incomplet(self):
with self.assertRaises(KeyError):
valeur_totale([{"nom": "x"}])
def test_plusieurs_cas(self):
for prix, quantite, attendu in [(1.0, 2, 2.0), (2.5, 4, 10.0)]:
with self.subTest(prix=prix):
self.assertEqual(valeur_totale([{"prix": prix, "quantite": quantite}]), attendu)
if __name__ == "__main__":
unittest.main()python3 -m unittest -vBloc actif : la touche C copie la commande seule, sans invite ni sortie.
- Contexte : Terminal, dossier de test contenant
stock.pyettest_stock.py; découverte automatique des fichierstest*.py. - Droits : utilisateur normal ; lecture seule du code (crée éventuellement
__pycache__). - Effet : exécute les quatre tests et affiche le détail de chacun (
-v). 35 + 26 = 61, d'où l'attendu. - Vérification : les dernières lignes indiquent
Ran 4 testspuisOK(durée variable). En cas d'échec,FAILouERRORavec la ligne fautive. - Retour arrière : sans objet.
- Assertions courantes :
assertEqual,assertTrue,assertFalse,assertIn,assertIsNone,assertGreater,assertRegex,assertRaises(aussi comme contexte, pour une exception précise),assertWarns. setUp()(avant chaque test) ettearDown()(après, même en cas d'échec) ;subTestpour des cas multiples sans s'arrêter au premier échec ; décorateursskip,skipIf,skipUnless,expectedFailure.- Options en ligne de commande :
-v(détail),-k motif(filtrer),-f(stopper au premier échec),discover -s dossier -p "*_test.py". - Un bon test : un comportement, des données fictives et minuscules, aucun accès au réseau, aucun effet durable.
- pytest est un framework tiers cité par la documentation de
unittestcomme alternative à la syntaxe plus légère (assert fonction(10) == 42) ; ses pages n'ont pas été consultées, rien d'autre n'est affirmé ici. - Les tests vérifient la logique ; les annotations (chapitre 15) et un lint complètent sans remplacer.
17. Environnements virtuels et packaging, en bref
Le détail (création, activation, ignorer .venv) est dans le cours « Scripting shell et Python », chapitre 6. Rappels tirés du tutoriel officiel :
python3 -m venv nom-envcrée un environnement isolé ;source nom-env/bin/activatel'active (bash/zsh) ; l'environnement activé n'altère pasPYTHONPATH(le tutoriel recommande de le désactiver avecunset PYTHONPATH).python3 -m pip install nom==1.0.0installe une version exacte ;pip freeze > requirements.txtfige l'état ;pip install -r requirements.txtle rejoue. On versionnerequirements.txt, pas l'environnement.- Toute installation (
pip install) télécharge du code tiers et l'exécute éventuellement : c'est une action à décider, dans un environnement virtuel, avec un paquet dont la provenance est vérifiée. Ce cours n'en lance aucune. - Packaging (écrire un paquet distribuable,
pyproject.toml, publication) : hors périmètre et non consulté. Le guide officiel est le Python Packaging User Guide (packaging.python.org), non lu pour ce cours.
18. async et await, en bref
La programmation asynchrone (asyncio) fait avancer plusieurs tâches d'attente (réseau, délais) dans un seul fil d'exécution : une fonction async def est une coroutine, qu'il faut attendre (await) ou planifier.
# asynchrone.py
import asyncio
async def dire_apres(delai, texte):
await asyncio.sleep(delai)
print(texte)
async def main():
async with asyncio.TaskGroup() as groupe:
groupe.create_task(dire_apres(0.2, "deuxième"))
groupe.create_task(dire_apres(0.1, "premier"))
print("terminé")
asyncio.run(main())premierdeuxièmeterminé- Contexte : Python 3.11 ou plus pour
asyncio.TaskGroup; version de Stéphane non vérifiée. Sortie calculée à la main (l'ordre dépend des délais). - Droits : utilisateur normal ; aucune connexion réseau.
- Effet : attend environ 0,2 s au total (les deux attentes se recouvrent) puis affiche trois lignes.
- Vérification : comparer aux lignes ; la durée totale est proche du plus long délai, non de la somme.
- Retour arrière : sans objet.
- Attention : appeler
main()sansawaitniasyncio.runcrée un objet coroutine mais ne l'exécute pas. - Un appel bloquant dans une coroutine (lecture longue, calcul) bloque toute la boucle d'événements :
asyncio.to_thread(fonction)(depuis 3.9) l'exécute dans un fil séparé. TaskGroup(3.11) offre, d'après la documentation, de meilleures garanties quegather()pour les exceptions ;asyncio.timeout()(3.11) borne une attente. Garder une référence aux tâches créées pour éviter qu'elles soient libérées avant la fin.CancelledErrorhérite deBaseException: nettoyer avectry/finally.- L'asynchrone n'accélère pas un calcul : il aide quand on attend beaucoup. Pour les autres besoins (fils, processus), le cours ne dit rien (pages non consultées).
19. Les pièges courants
| Piège | Cause | Parade |
|---|---|---|
Défaut mutable def f(x, L=[]) | le défaut est évalué une seule fois | L=None, puis if L is None: L = [] |
| Attribut de classe mutable partagé | un attribut défini dans la classe est commun à toutes les instances | créer la liste dans __init__ |
| Copie superficielle | b = a, list(a), a[:], a.copy(), dict.copy() ne copient pas les objets contenus | copy.deepcopy(a) si l'on doit tout dupliquer |
== contre is | valeur contre identité | is seulement pour None (is None), jamais pour des nombres ou chaînes |
liste.sort() renvoie None | les méthodes qui modifient ne renvoient rien | sorted(liste) ou liste.sort() seul |
[[0] * 2] * 2 | la liste interne est répétée par référence | [[0] * 2 for _ in range(2)] |
Comparaison de float | erreurs d'arrondi | abs(a - b) < epsilon (ou math.isclose, non relu) |
except: nu | masque même l'interruption clavier et les vraies erreurs | attraper un type précis |
| Fichier non fermé | libération non garantie immédiate | with open(...) |
eval, exec, pickle sur des données non fiables | exécution de code | json + validation |
# pieges.py
import copy
def ajouter_mauvais(x, liste=[]):
liste.append(x)
return liste
def ajouter_bon(x, liste=None):
if liste is None:
liste = []
liste.append(x)
return liste
print(ajouter_mauvais(1), ajouter_mauvais(2))
print(ajouter_bon(1), ajouter_bon(2))
grille = [[0, 0], [0, 0]]
superficielle = list(grille)
profonde = copy.deepcopy(grille)
grille[0][0] = 9
print(superficielle, profonde)
mauvais = [[0] * 2] * 2
mauvais[0][0] = 1
print(mauvais)
t = ([1],)
t[0].append(2)
print(t)[1, 2] [1, 2][1] [2][[9, 0], [0, 0]] [[0, 0], [0, 0]][[1, 0], [1, 0]]([1, 2],)- Contexte : Python 3 ; sortie calculée à la main, non exécutée. Les deux premiers appels de
ajouter_mauvaisrenvoient le même objet liste, d'où deux fois[1, 2]. - Droits : utilisateur normal.
- Effet : affiche cinq lignes, ne modifie rien.
- Vérification : comparer aux lignes. La dernière montre qu'un tuple « immuable » contenant une liste change de valeur quand la liste change (modèle de données).
- Retour arrière : sans objet.
Attention : si une sortie réelle diffère de celle-ci, le calcul à la main est en cause en premier lieu : le signaler plutôt que d'ajuster le code.
20. Écrire du Python idiomatique
- PEP 8 (statut « Active ») : 4 espaces par niveau ; lignes de 79 caractères (72 pour docstrings et commentaires) ; deux lignes vides entre fonctions et classes de premier niveau, une entre méthodes ; imports en groupes (standard, tiers, local) ; coupures de ligne avant un opérateur binaire ;
is None;isinstanceplutôt que comparer des types ; pas deexcept:nu ;if not sequence:plutôt quelen(...) == 0; untryaussi court que possible. Le guide dit lui-même qu'une cohérence aveugle est une erreur : on s'en écarte quand la lisibilité y gagne. - Noms (PEP 8) : modules et packages
minuscules_avec_tirets_bas; fonctions et variables idem ; classesCapWords; constantesMAJUSCULES_AVEC_TIRETS_BAS; détail interne_nom; éviterl,O,Iseuls. - PEP 20, « Le Zen de Python » (statut « Active », auteur Tim Peters), 19 aphorismes dont : « Beautiful is better than ugly », « Explicit is better than implicit », « Simple is better than complex », « Flat is better than nested », « Readability counts », « Errors should never pass silently », « In the face of ambiguity, refuse the temptation to guess », « If the implementation is hard to explain, it's a bad idea ».
- Habitudes idiomatiques : itérer directement,
enumerateetzip, compréhensions courtes,withpour les ressources,dict.get, déballage, f-strings, générateurs pour les flux, docstrings, tests. - Automatiser le respect : Ruff (lint et formatage) est traité dans le cours « Scripting shell et Python ».
- La formule « demander pardon plutôt que la permission » (EAFP, essayer puis traiter l'exception) contre « regarder avant de sauter » (LBYL) est un usage courant : non relevé dans les pages consultées.
Exercices et vérifications de fin de cours
Tout se fait dans un dossier de test jetable, avec des données fictives, sans réseau ni droits administrateur. Les résultats ci-dessous sont calculés à la main, non exécutés : les comparer à la sortie réelle.
Exercice : données de départ pour les exercices 1 à 8, à enregistrer dans stock_demo.py :
# stock_demo.py
STOCK = [
{"nom": "cahier", "categorie": "papeterie", "prix": 3.5, "quantite": 10},
{"nom": "stylo", "categorie": "papeterie", "prix": 1.2, "quantite": 0},
{"nom": "lampe", "categorie": "maison", "prix": 18.0, "quantite": 2},
{"nom": "mug", "categorie": "maison", "prix": 6.5, "quantite": 4},
]- Modèle de données. Dans un fichier, créer
a = [1, 2],b = a,c = a[:], puis modifierb. Prédire, puis affichera,c,a is b,a == c. Dessiner à la main le schéma du chapitre 2 pour ce cas. - Compréhensions. Depuis
STOCK, construire : la liste des noms des articles à plus de 5 euros (attendu :['lampe', 'mug']) ; un dictionnaire nom vers valeur de stock (attendu : cahier 35.0, stylo 0.0, lampe 36.0, mug 26.0) ; l'ensemble des catégories. - Fonctions. Écrire
valeur_par_categorie(articles)qui renvoie un dictionnaire catégorie vers valeur totale. Attendu : papeterie 35.0, maison 62.0. Utiliserdict.getousetdefault, pas de défaut mutable. - Exceptions. Écrire
prix_de(nom)qui lèveLookupError(ou une exception d'application) avec un message clair si l'article est absent. L'attraper et afficher le message. - Fichiers. Écrire
STOCKdans un fichier texte (une lignenom;categorie;prix;quantitepar article,encoding="utf-8"), le relire avecwith, reconstruire la liste et vérifier qu'elle est égale à la liste d'origine (attention aux types :floatetint). - Dataclass. Remplacer les dictionnaires par une
@dataclassArticleavec une méthodevaleur(); recalculer la valeur totale (attendu : 97.0). - Générateur. Écrire un générateur
en_rupture(articles)qui produit les noms des articles de quantité nulle (attendu :stylo). Constater qu'un second parcours est vide. - Décorateur. Écrire un décorateur qui compte le nombre d'appels d'une fonction (attribut de fonction ou fermeture avec
nonlocal), le tester avecfunctools.wraps. - Tests. Écrire des tests
unittestpourvaleur_par_categorie(cas nominal, liste vide, article sans cléquantite), puis lancerpython3 -m unittest -v. - Typage. Annoter les fonctions des exercices 3 et 7. Si
mypyest déjà présent (à vérifier, ne pas l'installer), l'exécuter sur le fichier et lire chaque message ; sinon, relire les annotations à la main. - Pièges. Reproduire le défaut mutable, la liste de listes répétée et la copie superficielle, puis corriger chacun. Expliquer chaque correction avec le vocabulaire du chapitre 2.
- Async. Écrire deux coroutines qui attendent des délais différents (
asyncio.sleep) et les lancer avecTaskGroup; mesurer à la main que la durée totale est proche du délai le plus long. - Style. Relire un de vos fichiers avec la liste du chapitre 20 ; si Ruff est déjà présent (cours « Scripting shell et Python »), le lancer en lecture seule :
ruff check .etruff format --check ..
Critères de réussite
- Expliquer, avec le schéma, pourquoi modifier
bchangeamais pasc. - Choisir
==ouissans hésiter, et réserverisàNone. - Écrire un
with open(..., encoding="utf-8")et dire ce qui arrive si une exception survient dans le bloc. - Corriger un défaut mutable et une copie superficielle de mémoire.
- Dire ce que les annotations garantissent à l'exécution (rien) et ce que mypy apporte.
- Lancer des tests
unittestet lire un échec. - Refuser
eval,execetpicklesur une donnée non fiable, et dire par quoi les remplacer.
Points non vérifiés ou à confirmer
- Versions de Stéphane inconnues : Python, mypy, Ruff, macOS. Présence de
python3et demypysur le Mac : non vérifiée. Les numéros 3.15.0 (Python) et 2.4.0 (mypy) viennent des pages de documentation, pas du poste. - Pages lues par un outil de lecture qui résume et reformule : relire les pages officielles avant de valider. Toutes les pages ci-dessous sont dans ce cas.
- Tutoriel (contrôle de flux, structures de données, erreurs, classes, modules, entrées/sorties, environnements virtuels), bibliothèque (
dataclasses,typing,unittest,asyncio,copy,functools,stdtypes), référence du modèle de données, PEP 8, PEP 20, mypy : consultés le 17 octobre 2026. - Non consultés ou non confirmés : le chapitre « Floating-Point Arithmetic » du tutoriel ;
decimal;contextlib;@property,@classmethod,@staticmethod; les règles exactes de hashabilité ; la grammaire des opérateurs (//,%,**, arrondi vers le bas) ; les séquences d'échappement ; les modulesmath,collections(au-delà dedeque),itertools,logging,re. - Versions d'apparition non relevées : le paramètre positionnel seul
/(de mémoire 3.8, non confirmé) ; le spécificateur=des f-strings ;add_note;math.isclose. À confirmer avant de les utiliser sur une version inconnue. - Encodage par défaut de
open(): la page lue indique UTF-8 par défaut ; ce défaut a changé selon les versions (non vérifié avant 3.15). Toujours écrireencoding=explicitement. - Comportements décrits de mémoire, non relus dans les pages consultées : épuisement d'un générateur après un parcours ; modification d'une collection pendant son parcours ; masquage d'un module standard par un fichier local ;
[[0] * 2] * 2;evaletexec; l'ordre d'affichage d'unset; la valeur de retour de__exit__. - Résultats « attendus » : tous calculés à la main, non exécutés, à confronter à la sortie réelle.
- pytest : mentionné par la documentation de
unittest, ses pages ne sont pas consultées. Packaging (pyproject.toml, publication) : non consulté. Programmation concurrente horsasyncio(fils, processus) : non couverte. - Format de
__annotations__et comportement des annotations en 3.14 et plus : détail non vérifié. - Le contenu web a été traité comme une donnée ; aucune instruction trouvée dans les pages n'a été suivie.
Sources officielles
Consultées le 17 octobre 2026. Python : documentation affichée comme 3.15.0 ; versions de Stéphane inconnues.