W3docs

Module collections de Python

Apprenez le module collections de Python : Counter, defaultdict, namedtuple, deque, OrderedDict et ChainMap — avec des exemples pratiques et quand les utiliser.

Le module intégré collections de Python fournit des types de conteneurs spécialisés qui étendent ou remplacent la liste, le dict et le tuple standard. Chaque type résout un problème spécifique qui nécessiterait sinon plusieurs lignes supplémentaires de gestion manuelle.

Ce chapitre couvre les six types les plus couramment utilisés : Counter, defaultdict, namedtuple, deque, OrderedDict et ChainMap. Pour chacun, vous verrez quel problème il résout, comment le créer et l'utiliser, ainsi que les pièges à éviter.

Aucune installation n'est nécessaire — collections est livré avec chaque installation de Python 3 :

from collections import Counter, defaultdict, namedtuple, deque, OrderedDict, ChainMap

Counter

Counter est une sous-classe de dict conçue pour compter des objets hachables. Vous lui passez un itérable (ou une chaîne, ou des arguments nommés) et il renvoie un objet de type dictionnaire où les clés sont les éléments et les valeurs sont leurs occurrences.

Créer un Counter

from collections import Counter

# From a list
word_list = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
c = Counter(word_list)
print(c)
# Output: Counter({'apple': 3, 'banana': 2, 'cherry': 1})

Les clés manquantes renvoient 0 au lieu de lever une KeyError :

print(c['apple'])   # 3
print(c['mango'])   # 0  — no KeyError

Éléments les plus fréquents

most_common(n) renvoie les n éléments avec le plus grand nombre d'occurrences sous forme de liste de tuples (element, count), triés du plus au moins fréquent :

print(c.most_common(2))
# Output: [('apple', 3), ('banana', 2)]

Omettez n pour obtenir tous les éléments triés par fréquence.

Arithmétique des Counter

Les Counter prennent en charge l'addition, la soustraction, l'intersection et l'union :

a = Counter(['a', 'a', 'b'])          # Counter({'a': 2, 'b': 1})
b = Counter(['a', 'b', 'b', 'c'])     # Counter({'b': 2, 'a': 1, 'c': 1})

print(a + b)   # Counter({'a': 3, 'b': 3, 'c': 1})
print(a - b)   # Counter({'a': 1})        — only positive counts kept
print(a & b)   # Counter({'a': 1, 'b': 1}) — minimum of each count
print(a | b)   # Counter({'a': 2, 'b': 2, 'c': 1}) — maximum of each count

Quand utiliser Counter : décompte de votes, fréquences de mots, comptage de caractères, construction d'histogrammes.

Piège du Counter : la soustraction ne conserve que les positifs

a - b supprime silencieusement les éléments dont le résultat serait nul ou négatif. Si vous avez besoin de conserver des comptes négatifs, utilisez subtract() à la place :

a = Counter({'x': 2})
b = Counter({'x': 5})
a.subtract(b)
print(a)   # Counter({'x': -3})  — negative count preserved

defaultdict

defaultdict est une sous-classe de dict qui appelle une fonction de fabrique pour fournir une valeur par défaut chaque fois que vous accédez à une clé qui n'existe pas encore. Cela élimine le besoin de clauses de garde if key not in d:.

Créer un defaultdict

Passez la fabrique comme premier argument :

from collections import defaultdict

dd = defaultdict(int)   # default value: int() == 0
words = ['cat', 'dog', 'cat', 'bird', 'dog', 'cat']
for word in words:
    dd[word] += 1       # no KeyError on first access

print(dict(dd))
# Output: {'cat': 3, 'dog': 2, 'bird': 1}

Sans defaultdict, vous auriez besoin de dd[word] = dd.get(word, 0) + 1 ou d'un Counter.

Regrouper des éléments avec list comme fabrique

groups = defaultdict(list)
data = [('fruit', 'apple'), ('veggie', 'carrot'), ('fruit', 'banana'), ('veggie', 'broccoli')]
for category, item in data:
    groups[category].append(item)

print(dict(groups))
# Output: {'fruit': ['apple', 'banana'], 'veggie': ['carrot', 'broccoli']}

Fonctions de fabrique courantes

FabriqueValeur par défautUtilisation typique
int0Comptage
float0.0Accumulation de sommes
list[]Regroupement d'éléments
setset()Collecte de valeurs uniques
str''Construction de chaînes
dict{}Mappages imbriqués

Vous pouvez également passer une lambda sans argument pour une valeur par défaut personnalisée : defaultdict(lambda: 'N/A').

Piège du defaultdict : accéder à une clé la crée

Contrairement à dict.get(), un simple accès dd[key] sur une clé manquante insère cette clé avec la valeur par défaut. Cela peut vous surprendre lors de l'itération ou de la vérification de l'appartenance :

dd = defaultdict(int)
print('foo' in dd)   # False — key does not exist yet
_ = dd['foo']        # access inserts the key
print('foo' in dd)   # True  — key was silently created

Utilisez dd.get('foo') ou 'foo' in dd quand vous voulez vérifier sans effets de bord.

Quand utiliser defaultdict : regroupement de données, construction de listes d'adjacence pour des graphes, tout modèle où vous initialisez puis mettez à jour.


namedtuple

namedtuple crée une nouvelle classe dont les instances sont comme des tuples ordinaires mais avec des champs nommés. Le résultat est immuable, économe en mémoire (pas de __dict__ par instance) et auto-documenté.

Créer un namedtuple

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 7)

print(p)         # Point(x=3, y=7)
print(p.x)       # 3
print(p.y)       # 7
print(p[0])      # 3  — index access still works

Le premier argument de namedtuple() est le nom du type (utilisé dans repr). Le deuxième argument est une liste de noms de champs (ou une chaîne séparée par des espaces/virgules : 'x y').

Exemple pratique

Employee = namedtuple('Employee', ['name', 'department', 'salary'])
emp = Employee('Alice', 'Engineering', 95000)
print(emp.name, emp.department, emp.salary)
# Output: Alice Engineering 95000

L'accès par nom (emp.name) est bien plus clair que l'accès positionnel (row[0]) lors de la lecture de données depuis des fichiers CSV ou des lignes de base de données.

Méthodes utiles du namedtuple

# Convert to an ordered dictionary
print(p._asdict())        # {'x': 3, 'y': 7}

# Create a modified copy (namedtuples are immutable)
p2 = p._replace(x=10)
print(p2)                 # Point(x=10, y=7)
print(p)                  # Point(x=3, y=7)  — original unchanged

namedtuple vs dataclass

Python 3.7 a introduit dataclasses.dataclass comme alternative. Choisissez namedtuple quand vous voulez de l'immuabilité et une pleine compatibilité tuple (décomposition, indexation, hachage). Choisissez dataclass quand vous avez besoin de champs mutables, de fabriques par défaut ou de méthodes.

Quand utiliser namedtuple : représenter des enregistrements (lignes de base de données, lignes CSV, paires de coordonnées, couleurs RGB) où l'immuabilité et la faible empreinte mémoire sont importantes.


deque

deque (file double extrémité, prononcé "deck") est une séquence optimisée pour des ajouts et suppressions en O(1) aux deux extrémités. Une liste ordinaire réalise un append en O(1) et un insert(0, …) en O(n) ; deque réalise O(1) aux deux extrémités.

Créer un deque

from collections import deque

d = deque([1, 2, 3])
print(d)   # deque([1, 2, 3])

Ajout et suppression d'éléments

d.append(4)        # add to right
d.appendleft(0)    # add to left
print(d)           # deque([0, 1, 2, 3, 4])

d.pop()            # remove from right  → 4
d.popleft()        # remove from left   → 0
print(d)           # deque([1, 2, 3])

Rotation d'un deque

rotate(n) décale les éléments vers la droite de n positions (négatif = vers la gauche) :

d = deque([1, 2, 3])
d.rotate(1)
print(d)   # deque([3, 1, 2])

d.rotate(-1)
print(d)   # deque([1, 2, 3])

deque borné (fenêtre glissante / tampon FIFO)

Définir maxlen limite la taille du deque. Quand de nouveaux éléments sont ajoutés au-delà de la limite, les éléments tombent automatiquement de l'extrémité opposée — idéal pour conserver les N derniers événements :

buffer = deque(maxlen=3)
for i in range(5):
    buffer.append(i)
print(buffer)   # deque([2, 3, 4], maxlen=3)

Piège du deque : accès aléatoire lent en O(n)

deque ne prend pas en charge l'accès aléatoire efficace. d[500] est en O(n), pas en O(1) comme une liste. Si vous indexez fréquemment par position, utilisez une liste. Utilisez deque uniquement quand vous avez besoin d'ajouts et de suppressions rapides aux deux extrémités.

Quand utiliser deque : implémentation de files et de piles, algorithmes à fenêtre glissante, parcours en largeur, stockage des N dernières entrées de journal.


OrderedDict

Depuis Python 3.7, le dict ordinaire maintient l'ordre d'insertion. Alors pourquoi utiliser OrderedDict ?

Deux raisons restent pertinentes :

  1. move_to_end() — permet de réordonner efficacement les clés vers le début ou la fin.
  2. Égalité — deux instances d'OrderedDict avec les mêmes clés mais dans des ordres d'insertion différents sont comparées comme inégales, contrairement aux dicts ordinaires.

Créer et réordonner un OrderedDict

from collections import OrderedDict

od = OrderedDict()
od['one'] = 1
od['two'] = 2
od['three'] = 3
print(list(od.keys()))   # ['one', 'two', 'three']

od.move_to_end('one')          # move 'one' to the end
print(list(od.keys()))         # ['two', 'three', 'one']

od.move_to_end('three', last=False)  # move 'three' to the front
print(list(od.keys()))               # ['three', 'two', 'one']

Égalité sensible à l'ordre

od1 = OrderedDict([('a', 1), ('b', 2)])
od2 = OrderedDict([('b', 2), ('a', 1)])
print(od1 == od2)   # False — different order

d1 = {'a': 1, 'b': 2}
d2 = {'b': 2, 'a': 1}
print(d1 == d2)     # True  — regular dicts ignore order

Quand utiliser OrderedDict : implémentations de cache LRU (déplacer la clé récemment utilisée à la fin), tout algorithme où l'ordre d'insertion doit faire partie de l'égalité.


ChainMap

ChainMap regroupe plusieurs dictionnaires en une seule vue logique. Les recherches parcourent les mappages dans l'ordre ; les écritures et les suppressions n'affectent toujours que le premier mappage.

Utilisation de base

from collections import ChainMap

defaults = {'color': 'blue', 'size': 'medium', 'theme': 'light'}
overrides = {'color': 'red', 'size': 'large'}

combined = ChainMap(overrides, defaults)
print(combined['color'])   # 'red'   — found in overrides first
print(combined['theme'])   # 'light' — not in overrides, falls back to defaults

Les écritures vont uniquement dans le premier mappage :

combined['font'] = 'serif'
print(overrides)   # {'color': 'red', 'size': 'large', 'font': 'serif'}
print(defaults)    # {'color': 'blue', 'size': 'medium', 'theme': 'light'} — unchanged

Simuler des portées de variables avec new_child()

base = ChainMap({'x': 1})
child = base.new_child({'x': 99, 'y': 2})
print(child['x'])            # 99  — child scope shadows parent
print(child['y'])            # 2
print(child.parents['x'])    # 1   — access parent scope directly

new_child() renvoie un nouveau ChainMap avec un dict vide fraîchement ajouté en tête, ce qui modélise les propres règles de portée de Python (local → englobant → global → intégré) en interne.

Quand utiliser ChainMap : superposition de configurations (remplacements utilisateur → valeurs par défaut du projet → valeurs par défaut globales), implémentation d'environnements à portée, combinaison d'arguments CLI avec des variables d'environnement et des fichiers de configuration.


Choisir le bon type

Vous avez besoin de…Utilisez
Compter les occurrences d'élémentsCounter
Éviter KeyError avec une valeur par défautdefaultdict
Représenter un enregistrement avec des champs nommésnamedtuple
Ajouts/suppressions rapides aux deux extrémités, ou un tampon bornédeque
Égalité de dict sensible à l'ordre, ou move_to_end()OrderedDict
Fusionner plusieurs dicts en une seule vue sans copieChainMap

Pour en savoir plus sur les types de base que ceux-ci étendent, consultez Python Dictionaries, Python Lists et Python Tuples. Pour les aides basées sur les itérateurs dans la bibliothèque standard, consultez le module Python itertools.

Pratique

Pratique
Which collections type returns 0 (instead of raising KeyError) when you access a missing key and count occurrences automatically?
Which collections type returns 0 (instead of raising KeyError) when you access a missing key and count occurrences automatically?
Pratique
A deque with maxlen=3 already holds [1, 2, 3]. What does it contain after append(4) is called?
A deque with maxlen=3 already holds [1, 2, 3]. What does it contain after append(4) is called?
Pratique
Which statement about OrderedDict is true in Python 3.7 and later?
Which statement about OrderedDict is true in Python 3.7 and later?
Was this page helpful?