W3docs

Compréhensions de dictionnaires et d'ensembles en Python

Maîtrisez les compréhensions de dictionnaires, d'ensembles et les expressions génératrices Python avec syntaxe, exemples et pièges courants.

Les compréhensions de dictionnaires, les compréhensions d'ensembles et les expressions génératrices étendent la syntaxe compacte de la compréhension de liste à d'autres structures de données. Ce chapitre explique en détail chaque forme — syntaxe, filtrage, imbrication, schémas pratiques, et quand les éviter.

Compréhensions de dictionnaires

Une compréhension de dictionnaire construit un nouveau dict à partir de n'importe quel itérable en une seule expression. Au lieu d'écrire une boucle for qui appelle d[key] = value à chaque itération, vous décrivez le mapping de façon concise entre accolades.

Syntaxe

new_dict = {key_expr: value_expr for item in iterable}

Ajoutez un filtre if optionnel après l'itérable :

new_dict = {key_expr: value_expr for item in iterable if condition}
PartieRôle
key_exprExpression qui produit chaque clé
value_exprExpression qui produit chaque valeur
itemVariable de boucle — prend chaque valeur de iterable à tour de rôle
if conditionFiltre optionnel — ignore les éléments où condition est False

Exemple de base : construire une carte de carrés

squares = {x: x ** 2 for x in range(1, 6)}
print(squares)
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

La boucle for équivalente :

squares = {}
for x in range(1, 6):
    squares[x] = x ** 2

Les deux produisent le même résultat ; la forme avec compréhension est plus concise et exprime l'intention d'un coup d'œil.

Filtrage des entrées

Conserver uniquement les paires qui satisfont une condition :

prices = {"apple": 1.20, "banana": 0.50, "orange": 0.80, "grape": 2.50}

expensive = {item: price for item, price in prices.items() if price >= 1.00}
print(expensive)
# {'apple': 1.2, 'grape': 2.5}

Transformation des valeurs

Appliquer une fonction ou une expression arithmétique à chaque valeur :

prices = {"apple": 1.20, "banana": 0.50, "orange": 0.80}

# Apply a 10% discount and round to 2 decimal places
discounted = {item: round(price * 0.9, 2) for item, price in prices.items()}
print(discounted)
# {'apple': 1.08, 'banana': 0.45, 'orange': 0.72}

Inversion des clés et des valeurs

Inverser un dictionnaire (fonctionne correctement quand toutes les valeurs sont uniques) :

capitals = {"France": "Paris", "Germany": "Berlin", "Japan": "Tokyo"}

inverted = {city: country for country, city in capitals.items()}
print(inverted)
# {'Paris': 'France', 'Berlin': 'Germany', 'Tokyo': 'Japan'}

Si les valeurs ne sont pas uniques, les entrées ultérieures écrasent les précédentes. N'utilisez ceci que lorsque vous êtes certain que les valeurs forment un mapping un-à-un.

Construction à partir de deux itérables avec zip()

zip() associe les éléments de deux séquences, ce qui facilite la construction d'un dictionnaire à partir de listes de clés et de valeurs séparées :

keys = ["name", "age", "city"]
values = ["Alice", 30, "Berlin"]

profile = {k: v for k, v in zip(keys, values)}
print(profile)
# {'name': 'Alice', 'age': 30, 'city': 'Berlin'}

C'est équivalent à dict(zip(keys, values)), mais la forme avec compréhension permet d'ajouter facilement un filtre ou de transformer des valeurs en même temps.

Normalisation des clés

Une tâche courante dans le monde réel est de nettoyer les clés d'un dictionnaire — par exemple, mettre toutes les clés en minuscules lors de la fusion de données provenant de sources différentes :

raw = {"Name": "Alice", "AGE": 30, "City": "Berlin"}

normalised = {k.lower(): v for k, v in raw.items()}
print(normalised)
# {'name': 'Alice', 'age': 30, 'city': 'Berlin'}

Expression de valeur conditionnelle (ternaire dans la valeur)

Vous pouvez utiliser une expression ternaire dans la position de la valeur pour choisir entre deux valeurs par élément :

scores = {"Alice": 95, "Bob": 62, "Carol": 78, "Dave": 45}

grades = {name: "pass" if score >= 70 else "fail" for name, score in scores.items()}
print(grades)
# {'Alice': 'pass', 'Bob': 'fail', 'Carol': 'pass', 'Dave': 'fail'}

Compréhensions de dictionnaires imbriquées

Vous pouvez imbriquer une compréhension pour traiter des données à plusieurs niveaux. Par exemple, extraire un champ d'une liste de dictionnaires imbriqués :

students = [
    {"name": "Alice", "score": 95, "grade": "A"},
    {"name": "Bob",   "score": 82, "grade": "B"},
    {"name": "Carol", "score": 91, "grade": "A"},
]

# Build a {name: score} mapping from the list
name_to_score = {s["name"]: s["score"] for s in students}
print(name_to_score)
# {'Alice': 95, 'Bob': 82, 'Carol': 91}

Gardez l'imbrication peu profonde. Si la logique devient difficile à suivre, extrayez une fonction auxiliaire ou utilisez une boucle for simple.

Compréhensions d'ensembles

Une compréhension d'ensemble construit un set en une seule expression. Comme les ensembles ne contiennent que des éléments uniques, les doublons sont supprimés automatiquement.

Syntaxe

new_set = {expression for item in iterable}
new_set = {expression for item in iterable if condition}

La seule différence visuelle avec une compréhension de dictionnaire est l'absence de deux-points — il y a une seule expression, et non une paire key: value.

Exemple de base : carrés uniques

numbers = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {x ** 2 for x in numbers}
print(unique_squares)
# {1, 4, 9, 16}

L'ordre de sortie n'est pas garanti — les ensembles ne sont pas ordonnés, ne comptez donc pas sur un ordre d'affichage particulier.

Déduplication avec transformation

Un schéma courant est de normaliser des chaînes et de dédupliquer en une seule étape :

tags = ["Python", "python", "PYTHON", "Data", "data", "DATA"]

unique_tags = {tag.lower() for tag in tags}
print(unique_tags)
# {'python', 'data'}

Filtrage avec une condition

words = ["cat", "elephant", "dog", "rhinoceros", "ant"]

long_words = {w for w in words if len(w) > 4}
print(long_words)
# {'elephant', 'rhinoceros'}

Trouver des éléments communs

Les compréhensions d'ensembles se combinent naturellement avec les opérations sur les ensembles :

list_a = [1, 2, 3, 4, 5, 5, 6]
list_b = [4, 5, 6, 7, 8]

set_a = {x for x in list_a}
set_b = {x for x in list_b}

common = set_a & set_b
print(common)
# {4, 5, 6}

Pour une conversion simple sans transformation, set(list_a) est plus simple. Utilisez une compréhension d'ensemble lorsque vous avez besoin de filtrer ou de transformer lors de la conversion.

Expressions génératrices

Une expression génératrice ressemble à une compréhension de liste mais utilise des parenthèses au lieu de crochets. Au lieu de construire toute la collection en mémoire d'un coup, elle produit une valeur à la fois, à la demande (paresseusement).

Syntaxe

gen = (expression for item in iterable)
gen = (expression for item in iterable if condition)

Pourquoi utiliser une expression génératrice ?

ScénarioCompréhension de listeExpression génératrice
Besoin d'itérer le résultat plusieurs foisOuiNon — les générateurs sont épuisés après un seul passage
Besoin d'un accès aléatoire par index (result[3])OuiNon
Le résultat est passé à sum(), max(), any(), etc.Fonctionne mais alloue une listePréférable — diffuse les valeurs sans construire une liste
Séquence très grande ou infiniePeut manquer de mémoireEfficace — une valeur à la fois

Exemple de base

# List comprehension — builds the entire list in memory
squares_list = [x ** 2 for x in range(1, 6)]
print(squares_list)   # [1, 4, 9, 16, 25]

# Generator expression — yields values one at a time
squares_gen = (x ** 2 for x in range(1, 6))
print(squares_gen)    # <generator object <genexpr> at 0x...>
print(list(squares_gen))  # [1, 4, 9, 16, 25]

L'objet générateur lui-même n'est pas la liste — vous le consommez en itérant dessus ou en le passant à une fonction.

Passage direct aux fonctions intégrées

Lorsque vous passez une expression génératrice comme seul argument à une fonction, vous pouvez omettre la paire de parenthèses supplémentaire :

total = sum(x ** 2 for x in range(1, 1001))
print(total)  # 333833500

maximum = max(len(word) for word in ["apple", "banana", "kiwi"])
print(maximum)  # 6

any_negative = any(x < 0 for x in [1, -2, 3])
print(any_negative)  # True

Avantage en mémoire

Pour de grandes quantités de données, une expression génératrice évite de tout charger en RAM :

# Simulate a large log file as a list of strings
log_lines = [f"ERROR line {i}" if i % 100 == 0 else f"INFO line {i}" for i in range(1_000_000)]

# Generator scans lines without building an intermediate list
error_count = sum(1 for line in log_lines if line.startswith("ERROR"))
print(error_count)  # 10000

Les générateurs sont épuisés après un seul passage

C'est le piège le plus courant :

gen = (x * 2 for x in range(5))

print(list(gen))  # [0, 2, 4, 6, 8]
print(list(gen))  # [] — the generator is now empty

Si vous avez besoin d'itérer le résultat plus d'une fois, utilisez soit une compréhension de liste, soit recréez le générateur.

Chaînage d'expressions génératrices

Les expressions génératrices peuvent être composées sans créer de listes intermédiaires. Chaque étape tire les valeurs de la précédente :

numbers = range(1, 11)

# Stage 1: filter even numbers
evens = (x for x in numbers if x % 2 == 0)

# Stage 2: square them
even_squares = (x ** 2 for x in evens)

print(list(even_squares))  # [4, 16, 36, 64, 100]

Aucune liste intermédiaire n'est créée — les valeurs circulent dans le pipeline une à la fois.

Choisir la bonne forme

Vous avez besoin de…Utilisez
Une liste de valeurs transformées ou filtrées[expr for item in it]
Un dictionnaire à partir de paires de valeurs{k: v for item in it}
Une collection sans doublons{expr for item in it}
Une itération en un seul passage efficace en mémoire(expr for item in it)
Une logique complexe à plusieurs instructions par élémentBoucle for simple

Pièges courants

Compréhension de dict vs. compréhension d'ensemble. Les deux utilisent des accolades {}. La différence est de savoir si vous écrivez key: value (dict) ou une seule expression (set). Un {} vide crée toujours un dict vide, pas un ensemble vide — utilisez set() pour un ensemble vide.

d = {}     # empty dict
s = set()  # empty set — NOT {}

Clés écrasées. Si l'expression de clé produit des doublons, les valeurs ultérieures écrasent silencieusement les précédentes :

data = [("a", 1), ("b", 2), ("a", 99)]
d = {k: v for k, v in data}
print(d)  # {'a': 99, 'b': 2} — first 'a' is gone

Portée des variables. En Python 3, la variable de boucle dans toute compréhension est locale à la compréhension et ne se propage pas dans la portée environnante :

x = "original"
result = {x: x.upper() for x in ["a", "b", "c"]}
print(x)  # 'original' — comprehension's x did not overwrite this

Limite de lisibilité. Si vous avez besoin de plus d'une condition if ou si l'expression est longue, une boucle for simple avec des noms de variables descriptifs est généralement plus claire :

# Hard to read
result = {k: v for k, v in data.items() if k.startswith("user_") if v is not None}

# Easier to read
result = {}
for k, v in data.items():
    if k.startswith("user_") and v is not None:
        result[k] = v

Sujets connexes

Pratique

Pratique
Which of the following statements about Python comprehensions is correct?
Which of the following statements about Python comprehensions is correct?
Was this page helpful?