Question 1Quelle est la façon la plus concise d'obtenir le compte d'occurrences par élément d'une liste nums en une ligne ?
collections — Counter / defaultdict / deque / namedtuple
Counter pour compter les occurrences, defaultdict pour les clés manquantes, deque pour des opérations O(1) aux deux bouts et un buffer circulaire, et namedtuple pour des enregistrements nommés.
Le module collections regroupe des structures de données spécialisées qui comblent les manques laissés par list / dict / tuple. Cet article passe en revue les quatre que tu utiliseras le plus souvent dans des projets réels : Counter / defaultdict / deque / namedtuple, dans cet ordre.
À quoi sert chacune des quatre
Chacune existe pour simplifier un cas pénible à écrire avec les types intégrés simples. Le tableau ci-dessous donne la vue d'ensemble, puis chaque section plonge dans son utilisation.
list / dict / tuple simples.| Type | Problème qu'il résout | Alternative simple |
|---|---|---|
| Counter | Compter les éléments un par un avec une boucle est verbeux | for + dict pour compter |
| defaultdict | Besoin de if x not in d: d[x] = ... avant d'utiliser une nouvelle clé | dict.setdefault(...) |
| deque | list.insert(0, ...) et list.pop(0) sont lents (O(n)) | list (ok pour de petites données) |
| namedtuple | Les éléments d'un tuple ne sont accessibles que par position | dataclass ou dict (plus lourd) |
Counter — compter les occurrences en une ligne
Counter est une classe qui prend un itérable (une list, une chaîne, etc.) et retourne un dict du nombre de fois où chaque élément apparaît. Avec un dict simple, tu écrirais une boucle qui vérifie si la clé existe déjà, l'initialise à 1 sinon, ou l'incrémente sinon — mais Counter fait le même travail en une ligne : Counter(list).
La valeur retournée est une sous-classe de dict, donc tu peux extraire les valeurs avec counter["apple"] exactement comme un dict normal. Il y a aussi .most_common(N), qui retourne une liste de tuples (élément, compte) dans l'ordre décroissant — parfait pour les classements.
most_common(N) retourne le top N par fréquence, donc tu peux écrire un classement en une ligne.defaultdict — initialiser automatiquement les clés manquantes
defaultdict est un dict qui insère automatiquement une valeur par défaut quand tu accèdes à une clé qui n'existe pas encore. Avec un dict simple, tu dois écrire la danse vérification d'existence + initialisation à chaque fois, comme if key not in d: d[key] = []. Avec defaultdict, tu passes une fonction qui produit la valeur initiale au moment de la création, et il appelle cette fonction automatiquement la première fois qu'on touche une clé manquante.
Les deux motifs que tu verras le plus sont defaultdict(list) (liste vide pour les clés manquantes) et defaultdict(int) (0 pour les clés manquantes). L'argument est une fonction qui retourne la valeur initiale quand elle est appelée sans arguments — list et int correspondent tous les deux (ils retournent [] et 0 respectivement), donc tu les passes directement.
list pour une liste vide, int pour 0, set pour un set vide.Quand choisir Counter vs defaultdict
Si tu veux juste compter des choses, Counter est plus concis (et tu obtiens most_common gratuitement). Si tu regroupes en listes ou en sets — tout cas où la valeur initiale est un conteneur vide — defaultdict(list) / defaultdict(set) est le meilleur choix.
deque — opérations en O(1) aux deux extrémités
deque (double-ended queue, file à double extrémité) est une structure de données semblable à list qui supporte l'ajout et le retrait aux deux extrémités. La list simple gère déjà les opérations à droite via append et pop, mais list.insert(0, x) et list.pop(0) décalent chaque élément d'une case en interne, donc elles finissent en O(n) (plus lentes à mesure que la liste grandit).
deque est conçu pour que les deux extrémités soient en O(1) (temps constant indépendamment de la longueur), ce qui en fait le bon choix pour les files, les buffers d'historique et garder les N entrées les plus récentes — partout où tu dois pousser et retirer des deux côtés. L'argument maxlen est particulièrement pratique : fixe une longueur max, et les ajouts au-delà de cette limite suppriment silencieusement l'élément à l'extrémité opposée, te donnant un buffer circulaire gratuitement.
deque(maxlen=N), tu obtiens un buffer circulaire qui retire automatiquement le plus ancien quand il est plein.| Méthode | Effet | Coût |
|---|---|---|
| append(x) | Ajoute à droite | O(1) |
| appendleft(x) | Ajoute à gauche | O(1) |
| pop() | Retire à droite | O(1) |
| popleft() | Retire à gauche | O(1) |
| maxlen=N | Fixe la longueur max (buffer circulaire) | L'extrémité opposée est supprimée automatiquement |
namedtuple — enregistrements légers qui nomment les champs du tuple
namedtuple est une fonction qui définit un tuple à champs nommés en une ligne. Un tuple simple comme (3, 4) n'est accessible que par position (p[0] / p[1]), donc le lecteur doit se rappeler quel emplacement est lequel. namedtuple te permet d'écrire p.x / p.y avec des noms parlants, ce qui est un grand gain de lisibilité.
Il reste compatible avec les tuples normaux — l'indexation p[0] et le dépaquetage *p fonctionnent toujours — donc tu peux ajouter des noms à du code existant basé sur tuple sans rien casser. C'est une extension légère.
_asdict() convertit en dict, ce qui s'intègre bien avec JSON et pprint.namedtuple vs dataclass
Si tu as juste besoin d'un enregistrement léger immuable, opte pour namedtuple. Si tu veux des méthodes, des valeurs par défaut et des annotations de type détaillées, dataclass (couvert dans deux articles) est le bon outil. La force de namedtuple est la compatibilité tuple — il est parfait pour remplacer des types de retour existants comme def f() -> tuple[int, int]: sans casser les appelants.
Extras de namedtuple — repr / index / _asdict
En plus de l'accès nommé style classe, namedtuple te donne l'accès par index compatible tuple, un repr lisible, et une méthode _asdict pour la conversion en dict. Tu utiliseras ceux-ci pour le débogage, la compatibilité avec des API existantes et la sérialisation JSON, entre autres.
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Après avoir créé defaultdict(list), que te donne le premier accès à une nouvelle clé d["x"] ?
Question 3Lequel correspond le mieux quand tu veux garder seulement les 5 éléments les plus récents ?