Les ensembles (Sets) en langage Python#
En mathématiques, un ensemble regroupe une collection d'objets distincts sans ordre particulier. Python implémente ce concept avec le type natif set.
Deux caractéristiques fondamentales distinguent les ensembles des listes :
1. L'unicité absolue : un ensemble ne peut jamais contenir de doublons. Tout doublon inséré est éliminé automatiquement.
2. L'absence d'ordre et d'indexation : les éléments ne sont pas indexés par des numéros (set[0] est interdit). En contrepartie, tester l'appartenance d'un élément (valeur in mon_set) s'effectue en temps constant \(\mathcal{O}(1)\), quelle que soit la taille de la collection !
1. Création et le piège de l'ensemble vide#
On crée un ensemble en énumérant ses éléments entre accolades {...} :
# Ensemble d'entiers avec doublons ignorés
nombres = {1, 2, 2, 3, 4, 4, 4, 5}
print(nombres) # {1, 2, 3, 4, 5}
# Déduplication instantanée d'une liste
emails_bruts = ["a@test.com", "b@test.com", "a@test.com", "c@test.com"]
emails_uniques = list(set(emails_bruts))
print(emails_uniques) # ['a@test.com', 'b@test.com', 'c@test.com']
[!WARNING] Le piège de l'ensemble vide :
En Python, la notation{}désigne historiquement un dictionnaire vide !
Pour instancier un ensemble vide, vous devez obligatoirement utiliser le constructeur :
2. Manipulation d'un ensemble : Ajout et Suppression#
Les ensembles sont mutables, mais chaque élément à l'intérieur doit impérativement être immuable et hachable :
langages = {"Python", "SQL"}
# 1. add() : insère un seul élément
langages.add("R")
langages.add("Python") # Déjà présent : ignoré sans erreur
# 2. update() : ajoute plusieurs éléments depuis un itérable
langages.update(["C", "Docker", "Git"])
# 3. remove() : supprime un élément (LÈVE KeyError s'il est absent)
langages.remove("Docker")
# 4. discard() : supprime un élément EN TOUTE SÉCURITÉ (ne plante pas s'il est absent)
langages.discard("Rust") # N'existe pas, mais aucune exception levée !
# 5. pop() : extrait et renvoie un élément arbitraire
element = langages.pop()
3. Les opérations ensemblistes mathématiques#
Python propose une double syntaxe : des méthodes explicites et des opérateurs symboliques concis.
flowchart TD
subgraph Union [Union : A | B]
U["Tous les éléments présents dans A OU dans B"]
end
subgraph Intersection [Intersection : A & B]
I["Éléments communs présents dans A ET dans B"]
end
subgraph Difference [Différence : A - B]
D["Éléments présents dans A mais ABSENTS de B"]
end
subgraph SymDiff [Différence Symétrique : A ^ B]
S["Éléments dans A ou B mais PAS dans les deux"]
end
Considérons deux profils d'étudiants et leurs compétences techniques :
A. L'Union : \(A \cup B\) (Opérateur |)#
Rassemble l'ensemble des éléments appartenant au moins à l'un des deux groupes :
print(groupe_a | groupe_b)
# {'Python', 'SQL', 'Tableau', 'Git', 'Docker', 'Linux'}
# Équivalent : groupe_a.union(groupe_b)
B. L'Intersection : \(A \cap B\) (Opérateur &)#
Isole les compétences partagées par les deux groupes :
C. La Différence : \(A \setminus B\) (Opérateur -)#
Isole les compétences du groupe A que le groupe B ne possède pas :
D. La Différence symétrique : \(A \Delta B\) (Opérateur ^)#
Isole les compétences exclusives à l'un ou l'autre groupe (exclut les éléments communs) :
print(groupe_a ^ groupe_b)
# {'SQL', 'Tableau', 'Docker', 'Linux'}
# Équivalent : groupe_a.symmetric_difference(groupe_b)
4. Tests d'inclusion : Sous-ensembles et Sur-ensembles#
Pour évaluer si une collection est intégralement incluse dans une autre :
prerequis = {"Python", "SQL"}
profil_candidat = {"Python", "SQL", "Git", "Docker", "Pandas"}
# issubset() : teste si A est inclus dans B
print(prerequis.issubset(profil_candidat)) # True (prerequis <= profil_candidat)
# issuperset() : teste si B englobe A
print(profil_candidat.issuperset(prerequis)) # True (profil_candidat >= prerequis)
# isdisjoint() : teste si deux ensembles n'ont aucun élément commun
groupe_c = {"Java", "Kotlin"}
print(prerequis.isdisjoint(groupe_c)) # True
5. Les ensembles immuables : frozenset#
Par défaut, un set est mutable : il ne peut donc pas être inclus dans un autre ensemble, ni servir de clé dans un dictionnaire.
Pour ces cas particuliers, Python propose frozenset : une version immuable et hachable :
# frozenset est immuable
droits_admin = frozenset(["LIRE", "ECRIRE", "SUPPRIMER"])
# Peut servir de clé dans un dictionnaire :
roles_permis = {
droits_admin: "Super-Utilisateur",
frozenset(["LIRE"]): "Visiteur"
}
6. Mise en pratique : Analyse de cohortes d'utilisateurs#
Concevons un script d'analyse marketing comparant les visiteurs du mois de Janvier et de Février pour identifier les nouveaux venus, les fidèles et les utilisateurs perdus :
janvier = {"alice@test.com", "bob@test.com", "charlie@test.com", "david@test.com"}
fevrier = {"charlie@test.com", "david@test.com", "emma@test.com", "felix@test.com"}
# 1. Utilisateurs fidèles (présents les 2 mois) : Intersection
fideles = janvier & fevrier
# 2. Nouveaux clients acquis en février : Différence (Février - Janvier)
nouveaux = fevrier - janvier
# 3. Utilisateurs perdus / churn (Janvier - Février)
perdus = janvier - fevrier
# 4. Audience totale cumulée : Union
total_unique = janvier | fevrier
print("=== ANALYSE DE RETENTION (ALGOJUNGLE) ===")
print(f"Audience unique globale : {len(total_unique)} contacts")
print(f"Clients fideles (retenus): {len(fideles)} ({fideles})")
print(f"Nouveaux inscrits : {len(nouveaux)} ({nouveaux})")
print(f"Utilisateurs perdus : {len(perdus)} ({perdus})")
7. Défi d'application#
Écrivez une fonction est_pangramme(phrase: str) -> bool qui vérifie si une phrase donnée contient au moins une fois toutes les 26 lettres de l'alphabet latin (ignorant la casse et les espaces). Utilisez les ensembles pour résoudre ce problème en seulement 2 lignes de code.
Commentaires