Accès rapide :
La vidéo
Présentation du type set
Instanciation d'un set
Vérifier la présence ou non d'une donnée dans un set
Ajouter des valeurs dans un set
Supprimer des valeurs d'un set
Parcourir un set de données
Les « Set Comprehensions » (ou set en compréhension, en français)
Aspects avancés de la gestion des ensembles (sets)
L'union de deux ensembles
L'intersection de deux ensembles
La différence de deux ensembles
La différence symétrique de deux ensembles
Compléments sur la manipulation de vos sets
Quelques fonctions applicables aux sets
Quelques méthodes de la classe set
Quelques opérateurs de la classe set
Typage de vos sets
Travaux pratiques
Les énoncés
Les corrections
Cette vidéo vous montre comment utiliser une collection de type set. Un set est une collection qui empêche d'avoir des doublons dans les données qui y sont stockées.
La classe set
représente un ensemble de valeur non ordonnées et sans doublons
de valeurs. Diverses opérations sont possibles sur vos sets : ajout de données, suppression de données, recherche de données...
Les sets proposent également des opérations mathématiques telles que les unions, les intersections, les différences et différences symétriques d'ensembles.
Pour définir un set vide, il faut passer par le constructeur de la classe set
.
>>> empty_set = set() >>> type(empty_set) <class 'set'> >>> empty_set set() >>> len(empty_set) 0 >>>
data = {}
définie un dictionnaire et non un set. Il est donc obligatoire de passer par le constructeur de la classe.
Vous pouvez aussi définir un set avec des valeurs initiales : dans ce cas, elles doivent être placées entre une paire d'accolades. Voici deux exemples de définition de sets, avec des types de données différents : notez la disparition des données en double.
>>> data = {"c", "c++", "java", "python", "perl", "python", "java"} >>> type(data) <class 'set'> >>> data {'python', 'c++', 'java', 'perl', 'c'} >>> len(data) 5 >>> data = {10, 20, 30, 40, 50, 60, 10, 20, 30, 40, 50, 60} >>> data {50, 20, 40, 10, 60, 30} >>>
Ce qui est remarquable, lors des affichages des sets, c'est que les données ne se retrouvent pas dans l'ordre initial.
Cela est dû au fait, que le type set
organisme à sa manière les données
afin de pouvoir très rapidement savoir si une donnée est déjà présente ou non dans le set. Vous ne pouvez donc pas simplement influer sur l'ordonnancement
des données d'un set.
Un set peut contenir des données de différentes natures. Voici un exemple contenant des entiers, des flottants, des chaînes de caractères...
>>> data = {10, 3.14, "essai", 10, 20, True, 3.14} >>> data {True, 3.14, 20, 'essai', 10} >>>
Vous pouvez savoir si une donnée est présente dans un set, ou non, en utilisant l'opérateur in
.
>>> data = {10, 20, 30, 10, 20, 30, 40} >>> 20 in data True >>> 50 in data False >>>
A contrario, vous pouvez vérifier si une information n'est pas présente dans la collection avec l'opérateur not in
.
Voici des exemples d'utilisation.
>>> data = {10, 20, 30, 10, 20, 30, 40} >>> 20 not in data False >>> 25 not in data True >>>
Contrairement à une liste, il n'y a donc pas de notion d'ordre dans un set. En conséquence, vous ne trouverez donc pas,
dans un set, l'équivalent strict de la méthode list.append(value)
,
qui ajoute « à la fin » de la collection.
A la place, la classe set
propose la méthode
add
, qui n'implique pas de sémantique de fin de la collection.
Voici un exemple d'utilisation
>>> data = {10, 20, 30, 10, 20, 30, 10, 20, 30, 40} >>> data {40, 10, 20, 30} >>> data.add(50) >>> data {40, 10, 50, 20, 30} >>> data.add(60) >>> data {40, 10, 50, 20, 60, 30} >>> data.add(50) # Donnée déjà existante >>> data {40, 10, 50, 20, 60, 30} >>>
Contrairement à une liste, l'opérateur del
n'est pas disponible sur un set, étant donné qu'il n'y a pas d'ordre.
Par contre, vous pouvez utiliser la méthode remove
qui supprime une valeur
du set.
KeyError
sera déclenchée.
>>> s = {10, 20, 30, 10, 20, 30, 10, 20, 30, 40} >>> s {40, 10, 20, 30} >>> s.remove(20) >>> s {40, 10, 30} >>> s.remove(50) Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 50 >>>
Si vous souhaitez supprimer toutes les valeurs présentes dans un set, vous pouvez aussi invoquer la méthode
clear
.
>>> data = {10, 20, 30, 40, 50} >>> data.clear() >>> data set() >>>
Comme la notion de position n'est pas envisageable sur un set, il n'est pas possible d'utiliser une boucle while
pour le parcourir.
Mais, bien entendu, vous pouvez utiliser la boucle for
.
La syntaxe est simple : on commence par le mot clé for
suivit du nom de la variable qui va recevoir une à une les valeurs de la collection, puis on
trouve le mot clé in
lui-même suivit de la collection à traiter et enfin un caractère :
introduit le bloc d'instructions à exécuter
à chaque tour de boucle. En voici un exemple d'utilisation.
1 2 3 4 5 6 7 |
data = {10, 20, 30, 10, 20, 30, 10, 20, 30, 40, 50} # On traite chaque élément du set, un par un for value in data: print(value) print("Parcours du set terminé") |
Et voici le résultat produit par l'exemple ci-dessus.
50 20 40 10 30 Parcours du set terminé
A l'instar des listes en compréhention, il est possible de définir le contenu d'un set par un algorithme : voici quelques exemples d'utilisation.
>>> s = {x ** 2 for x in range(10)} >>> s {0, 1, 64, 4, 36, 9, 16, 49, 81, 25} >>> 128 in s False >>> >>> languages = ["Python", "Java", "Python", "C", "C++", "Java", "Perl", "Ada"] >>> final = {lang.upper() for lang in languages if lang[0] == "P"} >>> for lang in final: ... print(lang) ... PERL PYTHON >>>
Le concept de set en Python est très proche de la notion d'ensemble dans la théorie des ensembles. D'ailleurs la traduction du mot anglais set en
français est bien ensemble. Du coup, la classe set
reprend quelques
opérations définies dans la théorie des ensembles.
L'union de deux ensembles consiste à fusionner les données de deux ensembles tout en garantissant de ne pas avoir de valeurs en doublons.
Le langage Python propose l'opérateur |
pour réaliser l'union de deux ensembles. Voici un exemple d'utilisation.
>>> s1 = {"java", "python", "java", "ada"} >>> s1 {'java', 'python', 'ada'} >>> s2 = {"c", "c++", "c#", "python"} >>> s2 {'c#', 'c++', 'python', 'c'} >>> all = s1 | s2 >>> all {'java', 'c', 'python', 'c++', 'ada', 'c#'} >>>
|
soient des sets.
Si ce n'est pas le cas, une exception sera déclenchée, comme le montre l'exemple ci-dessous.
>>> {"java", "python", "java", "ada"} | ("c", "c++", "c#", "python") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for |: 'set' and 'tuple' >>> ["java", "python", "java", "ada"] | {"c", "c++", "c#", "python"} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for |: 'list' and 'set' >>>
Vous pouvez néanmoins réaliser l'union d'un set et d'une autre collection de nature quelconque : pour ce faire, il faudra privilégier la méthode
union
de la classe
set
. Cela implique que la collection sur laquelle sera invoquée la méthode
est forcément un set. Voici quelques exemples d'utilisation.
>>> # Union d'un set avec un tuple >>> {"java", "python", "java", "ada"}.union(("c", "c++", "c#", "python")) {'c', 'c++', 'java', 'python', 'ada', 'c#'} >>> >>> # Union d'un set avec une liste >>> {"java", "python", "java", "ada"}.union(["c", "c++", "c#", "python"]) {'c', 'c++', 'java', 'python', 'ada', 'c#'} >>> >>> # Union d'un set avec un set >>> {"java", "python", "java", "ada"}.union({"c", "c++", "c#", "python"}) {'c++', 'java', 'python', 'c', 'ada', 'c#'} >>>
L'intersection de deux ensembles consiste à ne conserver que les données présentes dans les deux ensembles.
Le langage Python propose l'opérateur &
pour réaliser l'intersection de deux ensembles. Voici un exemple d'utilisation.
>>> s1 = {"java", "python", "java", "ada"} >>> s1 {'ada', 'java', 'python'} >>> s2 = {"c", "c++", "c#", "python"} >>> s2 {'c++', 'c#', 'c', 'python'} >>> inter = s1 & s2 >>> inter {'python'} >>>
&
soient des sets.
Si ce n'est pas le cas, une exception sera déclenchée, comme le montre l'exemple ci-dessous.
>>> {"java", "python", "java", "ada"} & ("c", "c++", "c#", "python") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for &: 'set' and 'tuple' >>> ["java", "python", "java", "ada"] & {"c", "c++", "c#", "python"} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for &: 'list' and 'set' >>>
Vous pouvez néanmoins réaliser l'intersection d'un set et d'une autre collection de nature quelconque : pour ce faire, il faudra privilégier la méthode
intersection
de la classe
set
. Cela implique que la collection sur laquelle sera invoquée la méthode
est forcément un set. Voici quelques exemples d'utilisation.
>>> # Intersection d'un set avec un tuple >>> {"java", "python", "java", "ada"}.intersection(("c", "c++", "c#", "python")) {'python'} >>> >>> # Intersection d'un set avec une liste >>> {"java", "python", "java", "ada"}.intersection(["c", "c++", "c#", "python"]) {'python'} >>> >>> # Intersection d'un set avec un set >>> {"java", "python", "java", "ada"}.intersection({"c", "c++", "c#", "python"}) {'python'} >>>
La différence de deux ensembles consiste à ne conserver que les données du premier ensemble qui ne sont pas présentes dans le second.
Le langage Python propose l'opérateur -
pour réaliser l'intersection de deux ensembles. Voici un exemple d'utilisation.
>>> s1 = {10, 20, 30, 40} >>> s2 = {20, 40, 60} >>> diff = s1 - s2 >>> diff {10, 30} >>>
-
soient des sets.
Si ce n'est pas le cas, une exception sera déclenchée, comme le montre l'exemple ci-dessous.
>>> {"java", "python", "java", "ada"} - ("c", "c++", "c#", "python") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for -: 'set' and 'tuple' >>> ["java", "python", "java", "ada"] - {"c", "c++", "c#", "python"} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for -: 'list' and 'set' >>>
Vous pouvez néanmoins calculer la différence d'un set et d'une autre collection de nature quelconque : pour ce faire, il faudra privilégier la méthode
difference
de la classe
set
. Cela implique que la collection sur laquelle sera invoquée la méthode
est forcément un set. Voici quelques exemples d'utilisation.
>>> # Différence d'un set avec un tuple >>> {"java", "python", "java", "ada"}.difference(("ada", "c", "c++", "java")) {'python'} >>> >>> # Différence d'un set avec une liste >>> {"java", "python", "java", "ada"}.difference(["ada", "c", "c++", "java"]) {'python'} >>> >>> # Différence d'un set avec un set >>> {"java", "python", "java", "ada"}.difference({"ada", "c", "c++", "java"}) {'python'} >>>
Pour faire simple, la différence symétrique de deux ensembles est l'opposé de l'intersection de ces mêmes ensembles.
Dit autrement, cela permet de récupérer les valeurs distinctes n'appartenant qu'à un seul des deux ensembles.
En Python, vous pouvez obtenir cette opération via l'opérateur ^
.
>>> s1 = {10, 20, 30, 40} >>> s2 = {20, 30, 40, 50} >>> sym_diff = s1 ^ s2 >>> sym_diff {10, 50} >>>
^
soient des sets.
Si ce n'est pas le cas, une exception sera déclenchée, comme le montre l'exemple ci-dessous.
>>> {"java", "python", "java", "ada"} ^ ("c", "c++", "c#", "python") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for ^: 'set' and 'tuple' >>> ["java", "python", "java", "ada"] ^ {"c", "c++", "c#", "python"} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for ^: 'list' and 'set' >>>
Vous pouvez néanmoins calculer la différence symétrique d'un set et d'une autre collection de nature quelconque : pour ce faire, il faudra privilégier la
méthode symmetric_difference
de la classe
set
. Cela implique que la collection sur laquelle sera invoquée la méthode
est forcément un set. Voici quelques exemples d'utilisation.
>>> # Différence symétrique d'un set avec un tuple >>> {"java", "python", "java", "ada"}.symmetric_difference(("ada", "c", "c++", "java")) {'c++', 'c', 'python'} >>> >>> # Différence symétrique d'un set avec une liste >>> {"java", "python", "java", "ada"}.symmetric_difference(["ada", "c", "c++", "java"]) {'c++', 'c', 'python'} >>> >>> # Différence symétrique d'un set avec un set >>> {"java", "python", "java", "ada"}.symmetric_difference({"ada", "c", "c++", "java"}) {'c++', 'c', 'python'} >>>
Il existe de nombreuses fonctions applicables aux sets Python. Nous pouvons notamment citer :
len
: cette fonction permet de calculer la taille d'un set
(mais nous l'avions déjà présentée avec les autres types de collections).
>>> data = {50, 10, 30, 20, 40, 20} >>> len(data) 5 >>>
max
: cette fonction permet de calculer la valeur maximale contenu dans
une collection de données (un set ou un autre type de collections).
>>> data = {10, 20, 30, 40, 50, 60} >>> max(data) 60 >>>
min
: cette fonction permet de calculer la valeur minimale contenu dans
une collection de données (un set ou un autre type de collections).
>>> data = {10, 20, 30, 40, 50, 60} >>> min(data) 10 >>>
sum
: cette fonction permet de sommer toutes les valeurs de la collection
passée en paramètre. Attention, les données doivent obligatoirement être numériques.
>>> data = {10, 20, 30, 40, 50, 60} >>> total = sum(data) >>> total 210 >>>
Vous vous en doutez, la classe set
expose aussi de nombreuses méthodes
bien utiles. En voici quelques-unes : vous pouvez cliquer sur le nom de la méthode pour en obtenir un descriptif plus détaillé.
Méthode | Description |
---|---|
add |
Ajoute une nouvelle valeur au set courant. |
clear |
Supprime toutes les valeurs stockées dans le set. |
copy |
Renvoie une copie « peu profonde » (shallow copy, en anglais) du set courant. |
difference |
Calcule la différence de deux ensembles. |
difference_update |
Supprime tous les éléments d'une autre collection dans le set courant. |
discard |
Retire la valeur spécifiée du set courant. |
intersection |
Calcule l'intersection de deux ensembles. |
intersection_update |
Permet de conserver dans un premier ensemble uniquement les données qui sont présentes dans un second ensemble. |
isdisjoint |
Indique si deux ensembles sont disjoints. |
issubset |
Indique si un ensemble est disjoint d'un autre. |
issuperset |
Indique si un ensemble contient un autre ensemble. |
pop |
Extrait la première valeur du set et la retourne. |
remove |
Retire la valeur spécifiée du set courant ou déclenche une erreur si cette valeur n'existe pas. |
symmetric_difference |
Calcule la différence symétrique de deux ensembles. |
symmetric_difference_update |
Calcule la différence symétrique de deux ensembles et stocke ce résultat dans le set courant. |
union |
Réalise l'union (la fusion) de deux sets. |
update |
Ajoute au set courant les valeurs du set passé en paramètre. |
De nombreux autres opérateurs sont aussi proposés par la classe set
.
On retrouve bien entendu les opérateurs d'union |
, d'intersection &
, de différence -
et de différence symétrique ^
,
l'opérateur in
et l'opérateur not in
.
Nous avons vu, dans les chapitres précédents, qu'il était possible de spécifier le type de vos variables lors de leurs déclarations.
Il en va de même pour vos sets et vous pouvez même indiquer le type des éléments qu'un set doit contenir.
Voici un exemple de code indiquant que la variable data
sera typée en tant que set de valeurs flottantes (set[float]
).
1 2 3 |
# Attention : Python 3.9 ou supérieur requis !!!
data: set[float] = {10, 20.2, 13.4}
print(data)
|
from typing import Set
. Il faudra alors utiliser le type Set[float]
. Voici un exemple d'utilisation.
1 2 3 4 5 |
from typing import Set # Syntaxe recommandée pour des versions antérieures à Python 3.9 data: Set[float] = {10, 20.2, 13.4} print(data) |
N'oubliez pas que certains IDEs peuvent vérifier les informations de typage : c'est le cas de PyCharm. Si vous tentez d'insérer autre chose qu'une valeur flottante dans le tuple ci-dessus, l'IDE pourra signaler cette incohérence. Voici une capture d'écran de PyCharm montrant cette capacité.
Vous pouvez aussi utiliser l'outil « mypy » pour vérifier la cohérence des types de votre programme. Voici un exemple de vérification.
[$> mypy test_typing.py test_typing.py:2: error: Argument 4 to <set> has incompatible type "str"; expected "float" test_typing.py:3: error: Argument 1 to "add" of "set" has incompatible type "str"; expected "float" Found 2 errors in 1 file (checked 1 source file) $>
Exercice 1 : en considérant la liste de chaînes de caractères suivante, faite en sorte de calculer une seconde liste sans doublons et triée par ordre alphabétique.
names = ["Jean", "Alphonse", "Gilbert", "Bruno", "Jacques", "Jean", "Kevin", "Guido", "Bruno", "Frank"]
Exercice 2 : en considérant la structure de données ci-dessous, veuillez afficher la liste ordonnée des noms de famille présents, sans doublons, bien entendu.
names = [ ("Anakin", "Skywalker"), ("Luke", "Skywalker"), ("Leia", "Skywalker"), ("Obi-Wan", "Kenobi"), ("Han", "Solo"), ("Ben", "Solo") ]
Exercice 3 : la méthode os.listdir(folder)
, du module
os
permet de récupérer les noms des fichiers et sous-dossiers contenu dans le dossier
spécifié en paramètre (sous forme d'une liste). Ecrire un programme qui affiche les noms de fichiers (ou de sous-dossiers) en commun dans deux dossiers.
Comme toujours, essayez de faire ces exercices sans regarder directement la correction ci-dessous. ;-)
Exercice 1 : voici comment supprimer les doublons de la liste de noms et l'afficher de manière triée.
1 2 3 4 5 6 7 8 9 10 11 12 |
names = ["Jean", "Alphonse", "Gilbert", "Bruno", "Jacques", "Jean", "Kevin", "Guido", "Bruno", "Frank"] # On génère un set à partir des prénoms proposés name_set = set(names) # On retransfert les noms uniques dans une liste pour les trier names = list(name_set) names.sort() # On affiche les noms uniques for name in names: print(name) |
Et voici le résultat produit par cet exemple.
Alphonse Bruno Frank Gilbert Guido Jacques Jean Kevin
Exercice 2 : voici comment extraire les noms de famille, supprimer les doublons et les afficher de manière triée. Pour moi, le plus simple et de passer par un set en compréhension avant de repasser les données sélectionnées à une liste pour la trier.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
names = [ ("Anakin", "Skywalker"), ("Luke", "Skywalker"), ("Leia", "Skywalker"), ("Obi-Wan", "Kenobi"), ("Han", "Solo"), ("Ben", "Solo") ] # On reconstruit une liste par dessus le set de noms de famille. family_names = list({person[1] for person in names}) # On trie la liste et on l'affiche family_names.sort() for name in family_names: print(name) |
Et voici le résultat produit par cet exemple.
Kenobi Skywalker Solo
Exercice 3 : voici comment déterminer les noms de fichiers (ou de sous-dossiers) en communs dans deux dossiers différents.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import os # On extrait les éléments contenus dans les deux dossiers à comparer. entries1 = os.listdir("/bin") entries2 = os.listdir("/usr/bin") # On réaliser l'intersection de deux ensembles. # Vous avez deux possibilités : common_files = set(entries1).intersection(entries2) # ou: common_files = set(entries1) & set(entries2) # On afficher les fichiers en communs print("Les fichiers en communs sont :") for file in common_files: print(file) |
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :