enum et dataclasses — Constantes nommées et classes de données
Remplacer les littéraux chaîne par Enum, numéroter automatiquement avec IntEnum + auto() et trier, générer le code répétitif via @dataclass, et utiliser field(default_factory=list).
Deux modules pour rendre explicite le sens des valeurs. enum te permet de remplacer les littéraux chaîne dispersés dans ton code par des constantes nommées, donc les fautes de frappe surfacent à l'exécution au lieu de passer en silence. dataclasses fournit un moyen de définir une "classe qui ne fait que contenir des données" en une ligne, en éliminant le code répétitif de __init__ / __eq__ / __repr__.
Enum — remplacer les littéraux chaîne par des constantes nommées
Quand des littéraux chaîne comme "paid" / "shipped" sont dispersés dans ton code, les fautes de frappe comme "shippped" passent inaperçues jusqu'à l'exécution, l'autocomplétion n'aide pas, et les refactorisations sont fragiles. Enum résout ça en définissant l'ensemble des constantes comme une classe — les noms que tu définis s'autocomplètent dans ton éditeur, les fautes de frappe lèvent immédiatement AttributeError, l'itération sur tous les membres marche d'office, et tu obtiens une valeur sûre à utiliser comme clé de dict ou étiquette de switch.
# Chaînes de statut dispersées dans le codeif status =="paid":ship_order(order_id)elif status =="shippped": # Faute de frappe — devrait être "shipped", mais tu ne le remarqueras qu'à l'exécutionnotify_shipped(order_id)elif status =="cancelled":refund(order_id)
Littéraux chaîne vs EnumLes chaînes brutes ne s'autocomplètent pas et les fautes de frappe passent jusqu'à l'exécution. Enumn'autorise que les noms que tu as définis, donc tu obtiens autocomplétion, détection de fautes, et itération complète des membres d'un coup.
from enum import EnumclassOrderStatus(Enum):PENDING="pending"PAID="paid"SHIPPED="shipped"CANCELLED="cancelled"# Accès aux membres : chaque membre porte un nom et une valeurprint(OrderStatus.PAID) # OrderStatus.PAIDprint(OrderStatus.PAID.name) # 'PAID'print(OrderStatus.PAID.value) # 'paid'# Compare avec ==status = OrderStatus.PAIDif status == OrderStatus.PAID:print("Paiement reçu")
Définis un statut de commande comme Enum et lis les infos des membres.
① Importe Enum depuis le module enum
② Définis une classe Enum OrderStatus avec les quatre membres PENDING / PAID / SHIPPED / CANCELLED (les valeurs sont les chaînes en minuscules "pending" / "paid" / "shipped" / "cancelled")
③ Affiche OrderStatus.PAID.name sous la forme PAID name : ◯◯
④ Affiche OrderStatus.PAID.value sous la forme PAID value : ◯◯
⑤ Affiche tous les noms de membres sous la forme Tous les membres : ◯ (tu peux itérer avec for s in OrderStatus)
(Si ton code s'exécute correctement, l'explication apparaîtra.)
Éditeur Python
Exécuter le code pour voir le résultat
Construis une fonction qui compare des membres d'Enum dans if/elif et retourne un message par statut. Brancher sur l'égalité membre à membre au lieu de littéraux chaîne est la façon la plus sûre de l'écrire.
① Définis OrderStatus de la Pratique 1
② Définis une fonction get_message(status) qui utilise if/elif sur chaque membre pour retourner : PENDING → "En attente de paiement", PAID → "Préparation de l'expédition", SHIPPED → "Expédié", CANCELLED → "Annulé"
③ Affiche get_message(OrderStatus.PAID) sous la forme PAID : ◯
④ Affiche get_message(OrderStatus.SHIPPED) sous la forme SHIPPED : ◯
Éditeur Python
Exécuter le code pour voir le résultat
IntEnum et auto — numérotation automatique et comparaison numérique
IntEnum est un Enum qui hérite de int, donc les membres se comparent et opèrent arithmétiquement comme des entiers. C'est le bon choix pour les constantes où l'ordre numérique compte — priorités, niveaux, rangs. auto() est une fonction qui assigne les valeurs automatiquement à la place de les écrire à la main : aligne des appels auto() et ils deviennent 1, 2, 3, 4... dans l'ordre de définition.
Quand les nombres spécifiques n'importent pas (seul l'ordre compte), auto() est plus sûr parce que ajouter ou retirer des membres n'oblige pas à réécrire les nombres à la main.
IntEnum + autoauto() numérote automatiquement 1, 2, 3, .... IntEnumse compare avec int, donc Priority.LOW < Priority.HIGH fonctionne — l'ordre numérique encode la priorité.
Définis un IntEnum Priority avec auto() et trie plusieurs tâches de la priorité haute à la basse. Confirme dans un scénario réel que auto() numérote automatiquement et que IntEnum peut comparer et trier comme int.
① Importe IntEnum et auto depuis enum
② Définis une classe IntEnum Priority avec quatre membres LOW / MEDIUM / HIGH / URGENT (valeurs toutes en auto())
③ Construis une liste de toutes les valeurs de membres et affiche sous la forme Numérotation : ◯ (en confirmant que auto() a assigné 1, 2, 3, 4)
④ Trie la liste de tâches [("Nettoyage", Priority.LOW), ("Réponses email", Priority.HIGH), ("Préparer les documents", Priority.URGENT), ("Révision", Priority.MEDIUM)]de la priorité haute à la basse, puis sous le titre Ordre d'exécution : affiche chaque tâche sous la forme - nom (priorité)
Éditeur Python
Exécuter le code pour voir le résultat
@dataclass — définir une classe contenant des données en une ligne
Écrire une "classe simple avec juste des champs" à la main signifie définir __init__ pour fixer les attributs, __eq__ (la méthode dunder appelée pour les comparaisons ==) pour l'égalité de contenu, __repr__ (la méthode dunder appelée par print et le REPL) pour un affichage lisible, et ainsi de suite — les mêmes motifs répétitifs alignés à chaque fois. Un seul décorateur @dataclass génère tout ça automatiquement à partir de tes déclarations de champs annotés en type.
En d'autres termes, aligner simplement des attributs annotés en type construit toute la classe — tu obtiens les mêmes fonctionnalités pour beaucoup moins de code que les classes écrites à la main.
Ce que @dataclass génère automatiquementIl suffit de déclarer des champs annotés en type et tu obtiens __init__ / __eq__ / __repr__ gratuitement. field(default_factory=list) te permet de fixer en sécurité des valeurs par défaut mutables comme une liste vide, et frozen=True rend la classe immuable (changements d'attributs bloqués).
Utilise default_factory pour les valeurs par défaut mutables
Essayer de mettre une liste par défaut avec @dataclass en écrivant items: list = [] lève une SyntaxError (ou un avertissement de dépréciation). C'est une protection contre le bug classique de "chaque instance finit par partager la même liste". Utilise plutôt field(default_factory=list), qui crée une nouvelle liste vide par instance, ce qui est sûr. Même idée pour dict / set : field(default_factory=dict).
Définis un Order avec @dataclass et confirme les __eq__ et __repr__ auto-générés.
① Importe dataclass et field depuis dataclasses
② Définis une classe Order décorée par @dataclass — quatre champs : id: int, customer: str, items: list = field(default_factory=list), is_paid: bool = False
③ Construis une instance avec Order(id=1234, customer="Alice", items=["apple", "banana"]) et affiche-la directement (le __repr__ auto-généré s'active)
④ Construis une seconde instance avec le même contenu et affiche le résultat de la comparaison avec == sous la forme Égal : True / False
⑤ Change is_paid de la première instance à True, puis compare à nouveau avec == et affiche sous la forme Égal après changement : True / False
Éditeur Python
Exécuter le code pour voir le résultat
QUIZ
Vérification des connaissances
Répondez à chaque question une par une.
Question 1Quel est l'avantage de remplacer la comparaison de littéral chaîne "paid" par un Enum ?
Question 2Quel est l'avantage de auto() dans IntEnum ?
Question 3Quelle est la bonne façon de mettre une liste par défaut sur un champ dans @dataclass ?