En

Ensembles en Python

16 exercices

À propos de Ensembles

Un set est une collection mutable et non ordonnée d'objets hachables. Les membres d'un ensemble doivent être distincts : les doublons ne sont pas autorisés. Ils peuvent contenir plusieurs types de données différents et même des structures imbriquées comme un tuple de tuples, tant que tous les éléments peuvent être hachés. Les ensembles existent également en version immuable, frozenset.

On utilise le plus souvent les ensembles pour supprimer rapidement les doublons d'autres structures de données ou groupes d'éléments. On s'en sert aussi pour des comparaisons efficaces quand la mise en séquence et le suivi des doublons ne sont pas nécessaires.

Comme les autres types de collections (dictionnaires, listes, tuples), les sets prennent en charge :

  • L'itération avec for item in <set>,
  • Le test d'appartenance avec in et not in,
  • Le calcul de la longueur avec len(), et
  • Les copies superficielles avec copy().

Les sets ne prennent pas en charge :

  • L'indexation, sous quelque forme que ce soit
  • Le classement par tri ou par insertion
  • Le découpage
  • La concaténation avec +

Vérifier l'appartenance dans un set a une complexité temporelle constante (en moyenne), contrairement à la vérification d'appartenance dans une list ou une string, dont la complexité temporelle augmente avec la longueur des données. Des méthodes comme <set>.union(), <set>.intersection() ou <set>.difference() ont elles aussi une complexité temporelle constante (en moyenne).

Construction d'un ensemble

Même s'il existe de nombreuses façons de créer des ensembles, les méthodes de construction les plus directes consistent à déclarer un littéral d'ensemble, à utiliser le constructeur de la classe set (set()) et à utiliser une compréhension d'ensemble.

Littéraux d'ensemble

Un set peut s'écrire directement sous forme de littéral d'ensemble, avec des accolades {} et des virgules entre les éléments. Les doublons sont ignorés silencieusement :

>>> one_element = {'➕'}
{'➕'}

>>> multiple_elements = {'➕', '🔻', '🔹', '🔆'}
{'➕', '🔻', '🔹', '🔆'}

>>> multiple_duplicates =  {'Hello!', 'Hello!', 'Hello!', 
                            '¡Hola!','Привіт!', 'こんにちは!', 
                            '¡Hola!','Привіт!', 'こんにちは!'}
{'こんにちは!', '¡Hola!', 'Hello!', 'Привіт!'}

Les littéraux d'ensemble utilisent les mêmes accolades que les littéraux dict, ce qui signifie qu'il faut utiliser set() pour créer un set vide.

Le constructeur d'ensemble

set() (le constructeur de la classe set) peut être appelé avec n'importe quel iterable passé en argument. On parcourt les éléments de l'iterable et on les ajoute un à un à l'ensemble. L'ordre des éléments n'est pas conservé et les doublons sont ignorés silencieusement :

# To create an empty set, the constructor must be used.
>>> no_elements = set()
set()

# The tuple is unpacked & each element is added.  
# Duplicates are removed.
>>> elements_from_tuple = set(("Parrot", "Bird", 
                               334782, "Bird", "Parrot"))
{334782, 'Bird', 'Parrot'}

# The list is unpacked & each element is added.
# Duplicates are removed.
>>> elements_from_list = set([2, 3, 2, 3, 3, 3, 5, 
                              7, 11, 7, 11, 13, 13])
{2, 3, 5, 7, 11, 13}

Compréhensions d'ensemble

Comme les lists et les dicts, les ensembles peuvent être créés par compréhension :

# First, a list with duplicates
>>> numbers = [1,2,3,4,5,6,6,5,4,8,9,9,9,2,3,12,18]

# This set comprehension squares the numbers divisible by 3
# Duplicates are removed.
>>> calculated = {item**2 for item in numbers if item % 3 == 0}
{9, 36, 81, 144, 324}

Pièges liés à la création d'ensembles

À cause de son comportement de « dépaquetage », utiliser le constructeur set avec une string peut surprendre :

# String elements (Unicode code points) are 
# iterated through and added *individually*.
>>> elements_string = set("Timbuktu")
{'T', 'b', 'i', 'k', 'm', 't', 'u'}

# Unicode separators and positioning code points 
# are also added *individually*.
>>> multiple_code_points_string = set('अभ्यास')
{'अ', 'भ', 'य', 'स', 'ा', '्'}

Rappel : les ensembles peuvent contenir différents types de données et des types imbriqués, mais tous les éléments d'un set doivent être hachables :

# Attempting to use a list for a set member throws a TypeError
>>> lists_as_elements = {['🌈','💦'], 
                        ['☁️','⭐️','🌍'], 
                        ['⛵️', '🚲', '🚀']}

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'


# Standard sets are mutable, so they cannot be hashed.
>>> sets_as_elements = {{'🌈','💦'}, 
                        {'☁️','⭐️','🌍'}, 
                        {'⛵️', '🚲', '🚀'}}

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'

Cependant, on peut créer un set de sets avec le type frozenset() :

# Frozensets don't have a literal form.
>>> set_1 = frozenset({'🌈','💦'})
>>> set_2 = frozenset({'☁️','⭐️','🌍'})
>>> set_3 = frozenset({'⛵️', '🚲', '🚀'})

>>> frozen_sets_as_elements = {set_1, set_2, set_3}
>>> frozen_sets_as_elements
{frozenset({'⛵️', '🚀', '🚲'}),
 frozenset({'🌈', '💦'}),
 frozenset({'☁️', '⭐️', '🌍'})}

Ajouter et supprimer des membres d'un ensemble

On peut ajouter ou supprimer des éléments d'un set avec les méthodes <set>.add(<item>) et <set>.remove(<item>). La méthode .remove(<item>) lève une KeyError si l'élément n'est pas présent dans le set :

>>> creatures = {'crow', 'duck', 'fish', 'monkey', 'elephant'}
>>> creatures.add('beaver')
>>> creatures.remove('duck')
>>> creatures
{'beaver', 'crow', 'elephant', 'fish', 'monkey'}

# Trying to remove an item that is not present raises a KeyError
>>> creatures.remove('bear')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  KeyError: 'bear'

Autres stratégies pour supprimer des membres d'un ensemble

  • <set>.discard(<item>) supprime un élément du set, mais ne lève pas de KeyError si l'élément n'est pas présent.
  • <set>.clear() supprime tous les éléments de l'ensemble.
  • <set>.pop() supprime et renvoie un élément arbitraire, et lève une KeyError si le set est vide.

Opérations sur les ensembles

Les ensembles possèdent des méthodes qui reproduisent en général les opérations mathématiques sur les ensembles. La plupart (mais pas toutes) de ces méthodes ont un équivalent sous forme d'opérateur. Les méthodes acceptent en général n'importe quel iterable en argument, tandis que les opérateurs exigent que les deux côtés de l'opération soient des set ou des frozenset.

Tester l'appartenance entre ensembles

La méthode <set>.isdisjoint(<other_collection>) permet de tester si les éléments d'un set ont des éléments en commun avec ceux d'un autre ensemble. Cette méthode accepte n'importe quel iterable ou set en argument. Elle renvoie True si les deux ensembles n'ont aucun élément en commun, et False s'ils partagent des éléments.

# Both mammals and additional_animals are lists.
>>> mammals = ['squirrel','dog','cat','cow', 'tiger', 'elephant']
>>> additional_animals = ['pangolin', 'panda', 'parrot', 
                          'lemur', 'tiger', 'pangolin']

# Animals is a dict.
>>> animals = {'chicken': 'white',
               'sparrow': 'grey',
               'eagle': 'brown and white',
               'albatross': 'grey and white',
               'crow': 'black',
               'elephant': 'grey', 
               'dog': 'rust',
               'cow': 'black and white',
               'tiger': 'orange and black',
               'cat': 'grey',
               'squirrel': 'black'}
               
# Birds is a set.
>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}

# Mammals and birds don't share any elements.
>>> birds.isdisjoint(mammals)
True

# There are also no shared elements between 
# additional_animals and birds.
>>> birds.isdisjoint(additional_animals)
True

# Animals and mammals have shared elements.
# **Note** The first object needs to be a set or converted to a set
# since .isdisjoint() is a set method.
>>> set(animals).isdisjoint(mammals)
False

Vérifier les sous-ensembles et les sur-ensembles

<set>.issubset(<other_collection>) permet de vérifier si chaque élément de <set> est aussi dans <other_collection>. La forme opérateur est <set> <= <other_set> :

# Set methods will take any iterable as an argument.
# All members of birds are also members of animals.
>>> birds.issubset(animals)
True

# All members of mammals also appear in animals.
# **Note** The first object needs to be a set or converted to a set
# since .issubset() is a set method.
>>> set(mammals).issubset(animals)
True

# Both objects need to be sets to use a set operator
>>> birds <= set(mammals)
False

# A set is always a loose subset of itself.
>>> set(additional_animals) <= set(additional_animals)
True

<set>.issuperset(<other_collection>) est l'inverse de .issubset(). Elle permet de vérifier si chaque élément de <other_collection> est aussi dans <set>. La forme opérateur est <set> >= <other_set> :

# All members of mammals also appear in animals.
# **Note** The first object needs to be a set or converted to a set
# since .issuperset() is a set method.
>>> set(animals).issuperset(mammals)
True

# All members of animals do not show up as members of birds.
>>> birds.issuperset(animals)
False

# Both objects need to be sets to use a set operator
>>> birds >= set(mammals)
False

# A set is always a loose superset of itself.
>>> set(animals) >= set(animals)
True

Sous-ensembles et sur-ensembles « stricts »

<set> < <other_set> et <set> > <other_set> servent à tester les sous-ensembles stricts. Un set est un sous-ensemble strict si (<set> <= <other_set>) ET (<set> != <other_set>) pour l'opérateur <.

Un set est un sur-ensemble strict si (<set> >= <other_set>) ET (<set> != <other_set>) pour l'opérateur >. Ces opérateurs n'ont pas d'équivalent sous forme de méthode :

>>> animal_names = {'albatross','cat','chicken','cow','crow','dog',
                   'eagle','elephant','sparrow','squirrel','tiger'}

>>> animals_also = {'albatross','cat','chicken','cow','crow','dog',
                   'eagle','elephant','sparrow','squirrel','tiger'}

>>> mammals = {'squirrel','dog','cat','cow', 'tiger', 'elephant'}
>>> birds   = {'crow','sparrow','eagle','chicken', 'albatross'}

>>> mammals < animal_names
True

>>> animal_names > birds
True

# A set is not a *proper subset* if set == other set.
>>> animals_also < animal_names
False

# A set is never a *proper subset* of itself
>>> animals_also < animals_also
False

Réunion d'ensembles

<set>.union(*<other iterables>) renvoie un nouveau set contenant les éléments de <set> et de tous les <other iterables>. La forme opérateur de cette méthode est <set> | <other set 1> | <other set 2> | ... | <other set n>.

>>> perennials = {'Asparagus', 'Broccoli', 'Sweet Potato', 'Kale'}
>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Summer Squash'}
>>> more_perennials = ['Radicchio', 'Rhubarb', 
                      'Spinach', 'Watercress']

# Methods will take any iterable as an argument.
>>> perennials.union(more_perennials)
{'Asparagus','Broccoli','Kale','Radicchio','Rhubarb',
'Spinach','Sweet Potato','Watercress'}

# Operators require sets.
>>> set(more_perennials) | perennials
{'Asparagus',
 'Broccoli',
 'Kale',
 'Radicchio',
 'Rhubarb',
 'Spinach',
 'Sweet Potato',
 'Watercress'}

Différences d'ensembles

<set>.difference(*<other iterables>) renvoie un nouveau set contenant les éléments du <set> d'origine qui ne sont pas dans <others>. La version opérateur de cette méthode est <set> - <other set 1> - <other set 2> - ... - <other set n>.

>>> berries_and_veggies = {'Asparagus', 
                          'Broccoli', 
                          'Watercress', 
                          'Goji Berries', 
                          'Goose Berries', 
                          'Ramps', 
                          'Walking Onions', 
                          'Blackberries', 
                          'Strawberries', 
                          'Rhubarb', 
                          'Kale', 
                          'Artichokes', 
                          'Currants'}

>>> veggies = ('Asparagus', 'Broccoli', 'Watercress', 'Ramps',
               'Walking Onions', 'Rhubarb', 'Kale', 'Artichokes')

# Methods will take any iterable as an argument.
>>> berries = berries_and_veggies.difference(veggies)
{'Blackberries','Currants','Goji Berries',
 'Goose Berries', 'Strawberries'}

# Operators require sets.
>>> berries_and_veggies - berries
{'Artichokes','Asparagus','Broccoli','Kale',
'Ramps','Rhubarb','Walking Onions','Watercress'}

Intersections d'ensembles

<set>.intersection(*<other iterables>) renvoie un nouveau set contenant les éléments communs au set d'origine et à tous les <others> (autrement dit, le set où tout se recoupe). La version opérateur de cette méthode est <set> & <other set> & <other set 2> & ... & <other set n>

>>> perennials = {'Annatto','Asafetida','Asparagus','Azalea',
                 'Winter Savory', 'Broccoli','Curry Leaf','Fennel', 
                 'Kaffir Lime','Kale','Lavender','Mint','Oranges',
                 'Oregano', 'Tarragon', 'Wild Bergamot'}

>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Marjoram', 
              'Summer Squash', 'Okra','Shallots', 'Basil', 
              'Cilantro', 'Cumin', 'Sunflower', 'Chervil', 
              'Summer Savory'}

>>> herbs = ['Annatto','Asafetida','Basil','Chervil','Cilantro',
            'Curry Leaf','Fennel','Kaffir Lime','Lavender',
            'Marjoram','Mint','Oregano','Summer Savory', 
            'Tarragon','Wild Bergamot','Wild Celery',
            'Winter Savory']


# Methods will take any iterable as an argument.
>>> perennial_herbs = perennials.intersection(herbs)
{'Annatto', 'Asafetida', 'Curry Leaf', 'Fennel', 'Kaffir Lime',
 'Lavender', 'Mint', 'Oregano', 'Wild Bergamot','Winter Savory'}

# Operators require both groups be sets.
>>> annuals & set(herbs)
 {'Basil', 'Chervil', 'Marjoram', 'Cilantro'}

Différences symétriques d'ensembles

<set>.symmetric_difference(<other iterable>) renvoie un nouveau set qui contient les éléments présents dans <set> OU dans <other>, mais pas dans les deux. La version opérateur de cette méthode est <set> ^ <other set>.

>>> plants_1 = {'🌲','🍈','🌵', '🥑','🌴', '🥭'}
>>> plants_2 = ('🌸','🌴', '🌺', '🌲', '🌻', '🌵')


# Methods will take any iterable as an argument.
>>> fruit_and_flowers = plants_1.symmetric_difference(plants_2)
>>> fruit_and_flowers
{'🌸', '🌺', '🍈', '🥑', '🥭','🌻' }


# Operators require both groups be sets.
>>> fruit_and_flowers ^ plants_1
{'🌲',  '🌸', '🌴', '🌵','🌺', '🌻'}

>>> fruit_and_flowers ^ set(plants_2)
{'🥭', '🌴', '🌵', '🍈', '🌲', '🥑'}
Note

Une différence symétrique de plus de deux ensembles donne un set qui contient à la fois les éléments propres à chaque set ET les éléments partagés par plus de deux ensembles de la série (détails dans l'article Wikipédia sur la différence symétrique).

Pour n'obtenir que les éléments propres à chaque set de la série, il faut regrouper les intersections de toutes les combinaisons de 2 ensembles dans une étape séparée, puis les retirer :

>>> one = {'black pepper','breadcrumbs','celeriac','chickpea flour',
           'flour','lemon','parsley','salt','soy sauce',
           'sunflower oil','water'}

>>> two = {'black pepper','cornstarch','garlic','ginger',
           'lemon juice','lemon zest','salt','soy sauce','sugar',
           'tofu','vegetable oil','vegetable stock','water'}

>>> three = {'black pepper','garlic','lemon juice','mixed herbs',
             'nutritional yeast', 'olive oil','salt','silken tofu',
             'smoked tofu','soy sauce','spaghetti','turmeric'}

>>> four = {'barley malt','bell pepper','cashews','flour',
            'fresh basil','garlic','garlic powder', 'honey',
            'mushrooms','nutritional yeast','olive oil','oregano',
            'red onion', 'red pepper flakes','rosemary','salt',
            'sugar','tomatoes','water','yeast'}

>>> intersections = (one & two | one & three | one & four | 
                     two & three | two & four | three & four)
...
{'black pepper','flour','garlic','lemon juice','nutritional yeast', 
'olive oil','salt','soy sauce', 'sugar','water'}

# The ^ operation will include some of the items in intersections, 
# which means it is not a "clean" symmetric difference - there
# are overlapping members.
>>> (one ^ two ^ three ^ four) & intersections
{'black pepper', 'garlic', 'soy sauce', 'water'}

# Overlapping members need to be removed in a separate step
# when there are more than two sets that need symmetric difference.
>>> (one ^ two ^ three ^ four) - intersections
...
{'barley malt','bell pepper','breadcrumbs', 'cashews','celeriac',
  'chickpea flour','cornstarch','fresh basil', 'garlic powder',
  'ginger','honey','lemon','lemon zest','mixed herbs','mushrooms',
  'oregano','parsley','red onion','red pepper flakes','rosemary',
  'silken tofu','smoked tofu','spaghetti','sunflower oil', 'tofu', 
  'tomatoes','turmeric','vegetable oil','vegetable stock','yeast'}
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet

Apprends Ensembles

L'entraînement est verrouillé

Déverrouille 4 exercices de plus pour t'entraîner sur Ensembles