Participer au site avec un Tip
Rechercher
 

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 :

Manipulation de sets en Python

Manipulation de tuples Manipulation de dictionnaires


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

La vidéo

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.


Manipulation de sets en Python.

Présentation du type set

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.

Instanciation d'un set

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
>>> 
Exemple de définition d'un set vierge.
la syntaxe 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}
>>> 
Exemple de définitions de sets avec des données de différents types.

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}
>>> 
Exemple de définitions d'un set avec des données hétérogènes.

Vérifier la présence ou non d'une donnée dans un set

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
>>> 
Utilisation de l'opérateur in

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
>>> 
Utilisation de l'opérateur not in

Ajouter des valeurs dans un set

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}
>>>
Exemple d'ajout de valeurs dans un set.

Supprimer des valeurs d'un set

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.

si la donnée à supprimer n'est pas présente dans la collection, une exception de type 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()
>>>
Suppression de toutes les données du set

Parcourir un set de données

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é")
Fichier sample.py : exemple de parcours d'un set avec une boucle for

Et voici le résultat produit par l'exemple ci-dessus.

50
20
40
10
30
Parcours du set terminé

Les « Set Comprehensions » (ou set en compréhension, en français)

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
>>>
si cette syntaxe n'est pas encore claire et naturelle pour vous, vous retrouverez des compléments d'informations dans le chapitre dédié aux listes en compréhension.

Aspects avancés de la gestion des ensembles (sets)

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

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#'}
>>> 
cet opérateur nécessite que les deux opérandes (les deux parties) de l'opérateur | 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

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'}
>>> 
cet opérateur nécessite que les deux opérandes (les deux parties) de l'opérateur & 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

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}
>>> 
cet opérateur nécessite que les deux opérandes (les deux parties) de l'opérateur - 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'}
>>> 

La différence symétrique de deux ensembles

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}
>>> 
cet opérateur nécessite que les deux opérandes (les deux parties) de l'opérateur ^ 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'}
>>> 

Compléments sur la manipulation de vos sets

Quelques fonctions applicables aux sets

Il existe de nombreuses fonctions applicables aux sets Python. Nous pouvons notamment citer :

Quelques méthodes de la classe set

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.

Quelques opérateurs de la classe set

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.

Typage de vos sets

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)
Typage d'une variable en tant que liste de valeurs flottantes.
cette syntaxe, permettant de contrôler le type des éléments contenus dans le set, est disponible en Python depuis sa version 3.9. Si vous utilisez une version antérieure, il faudra adapter le code en utilisant un module de code supplémentaire via l'instruction suivante 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)
Typage d'une variable en tant que liste de valeurs flottantes.

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é.

Contrôle de typage par PyCharm.

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)
$> 
Contrôle de typage par l'intermédiaire de l'outil mypy.

Travaux pratiques

Les énoncés

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. ;-)

Les corrections

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)
Correction de l'exercice 1

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)
Correction de l'exercice 2

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)
Correction de l'exercice 3


Manipulation de tuples Manipulation de dictionnaires