Un ensemble est une collection mutable et non ordonnée d'objets hachables.
Les éléments 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 aussi en version immuable : les frozensets.
Les ensembles servent le plus souvent à supprimer rapidement les doublons d'autres structures de données ou de groupes d'éléments. Ils servent aussi à effectuer des comparaisons efficaces quand le séquencement et le suivi des doublons ne sont pas nécessaires.
Comme les autres types de collections (dictionnaires, tableaux, tuples), les sets prennent en charge :
for item in <set>
in et not in,len(), etcopy()
Les sets ne prennent pas en charge :
+
Vérifier l'appartenance à un set a une complexité temporelle constante (en moyenne), contrairement à la vérification d'appartenance dans une list ou une string, où la complexité temporelle croît avec la taille des données.
Les méthodes telles que <set>.union(), <set>.intersection() ou <set>.difference() ont elles aussi une complexité temporelle constante (en moyenne).
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 silencieusement ignorés :
>>> one_element = {'➕'}
{'➕'}
>>> multiple_elements = {'➕', '🔻', '🔹', '🔆'}
{'➕', '🔻', '🔹', '🔆'}
>>> multiple_duplicates = {'Hello!', 'Hello!', 'Hello!',
'¡Hola!','Привіт!', 'こんにちは!',
'¡Hola!','Привіт!', 'こんにちは!'}
{'こんにちは!', '¡Hola!', 'Hello!', 'Привіт!'}
Les littéraux d'ensembles utilisent les mêmes accolades que les littéraux dict, ce qui signifie qu'il faut utiliser set() pour créer un set vide.
set() (le constructeur de la classe set) peut être utilisé avec n'importe quel iterable passé en argument.
Les éléments de l'iterable sont parcourus un à un et ajoutés individuellement au set.
L'ordre des éléments n'est pas préservé et les doublons sont silencieusement ignorés :
# 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}
À cause de son comportement de « dépaquetage », utiliser 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('अभ्यास')
{'अ', 'भ', 'य', 'स', 'ा', '्'}
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'
Les ensembles disposent de méthodes qui imitent généralement les opérations mathématiques sur les ensembles.
La plupart (mais pas toutes) de ces méthodes ont un opérateur équivalent.
Les méthodes prennent généralement n'importe quel iterable en argument, tandis que les opérateurs exigent que les deux côtés de l'opération soient des sets ou des frozensets.
La méthode <set>.isdisjoint(<other_collection>) sert à vérifier si les éléments d'un set ont un quelconque recoupement avec les éléments d'un autre set.
La 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 si des éléments sont partagés.
Il n'existe pas d'opérateur équivalent :
# 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
<set>.issubset(<other_collection>) sert à vérifier si chaque élément de <set> se trouve aussi dans <other_collection>.
La forme opérateur est <set> <= <other_set> :
# 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'}
# 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 sert à vérifier si chaque élément de <other_collection> se trouve 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
<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 s'intersecte).
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'}
<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'}
<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'}
<set>.symmetric_difference(<other iterable>) renvoie un nouveau set contenant les éléments qui sont 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)
{'🥭', '🌴', '🌵', '🍈', '🌲', '🥑'}
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 entre 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 entre 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'}
Avec tes associés, tu diriges une petite entreprise de traiteur. Tu viens d'accepter d'organiser un événement pour un club de cuisine local, autour de plats « favoris du club ». Le club manque d'expérience dans l'organisation de grands événements et a besoin d'aide pour organiser, faire les courses, préparer et servir. Tu as donc décidé d'écrire quelques petits scripts Python pour accélérer toute la planification.
Les recettes de l'événement proviennent de sources diverses et leurs ingrédients semblent comporter des doublons (voire plus), et tu ne veux sûrement pas finir par acheter des quantités superflues ! Avant que les courses et la cuisine puissent commencer, la liste d'ingrédients de chaque plat doit être « nettoyée ».
Implémente la fonction clean_ingredients(<dish_name>, <dish_ingredients>), qui prend le nom d'un plat et une list d'ingrédients.
Cette fonction doit renvoyer un tuple dont le premier élément est le nom du plat, suivi du set dédupliqué des ingrédients.
>>> clean_ingredients('Punjabi-Style Chole', ['onions', 'tomatoes', 'ginger paste', 'garlic paste', 'ginger paste', 'vegetable oil', 'bay leaves', 'cloves', 'cardamom', 'cilantro', 'peppercorns', 'cumin powder', 'chickpeas', 'coriander powder', 'red chili powder', 'ground turmeric', 'garam masala', 'chickpeas', 'ginger', 'cilantro'])
>>> ('Punjabi-Style Chole', {'garam masala', 'bay leaves', 'ground turmeric', 'ginger', 'garlic paste', 'peppercorns', 'ginger paste', 'red chili powder', 'cardamom', 'chickpeas', 'cumin powder', 'vegetable oil', 'tomatoes', 'coriander powder', 'onions', 'cilantro', 'cloves'})
L'événement proposera à la fois des cocktails et des « mocktails », des boissons mélangées sans alcool. Tu dois t'assurer que les boissons « mocktail » sont réellement sans alcool et que les cocktails contiennent bel et bien de l'alcool.
Implémente la fonction check_drinks(<drink_name>, <drink_ingredients>), qui prend le nom d'une boisson et une list d'ingrédients.
La fonction doit renvoyer le nom de la boisson suivi de « Mocktail » si la boisson ne contient aucun ingrédient alcoolisé, et le nom de la boisson suivi de « Cocktail » si elle contient de l'alcool.
Pour les besoins de cet exercice, les cocktails ne contiendront que des alcools issus de la constante ALCOHOLS définie dans sets_categories_data.py :
>>> from sets_categories_data import ALCOHOLS
>>> check_drinks('Honeydew Cucumber', ['honeydew', 'coconut water', 'mint leaves', 'lime juice', 'salt', 'english cucumber'])
...
'Honeydew Cucumber Mocktail'
>>> check_drinks('Shirley Tonic', ['cinnamon stick', 'scotch', 'whole cloves', 'ginger', 'pomegranate juice', 'sugar', 'club soda'])
...
'Shirley Tonic Cocktail'
La liste des invités comprend des convives aux besoins alimentaires variés, et ton personnel devra répartir les plats entre Vegan, Vegetarian, Paleo, Keto et Omnivore. Un plat n'appartient à une catégorie que si tous ses ingrédients figurent dans l'ensemble d'ingrédients de cette catégorie.
Implémente la fonction categorize_dish(<dish_name>, <dish_ingredients>), qui prend le nom d'un plat et un set contenant les ingrédients de ce plat.
La fonction doit renvoyer une string de la forme dish name: <CATEGORY> (la catégorie à laquelle le plat appartient).
Tous les plats fournis « rentreront » dans l'une des catégories importées depuis sets_categories_data.py (VEGAN, VEGETARIAN, PALEO, KETO ou OMNIVORE).
>>> from sets_categories_data import VEGAN, VEGETARIAN, PALEO, KETO, OMNIVORE
>>> categorize_dish('Sticky Lemon Tofu', {'tofu', 'soy sauce', 'salt', 'black pepper', 'cornstarch', 'vegetable oil', 'garlic', 'ginger', 'water', 'vegetable stock', 'lemon juice', 'lemon zest', 'sugar'})
...
'Sticky Lemon Tofu: VEGAN'
>>> categorize_dish('Shrimp Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', {'shrimp', 'bacon', 'avocado', 'chickpeas', 'fresh tortillas', 'sea salt', 'guajillo chile', 'slivered almonds', 'olive oil', 'butter', 'black pepper', 'garlic', 'onion'})
...
'Shrimp Bacon and Crispy Chickpea Tacos with Salsa de Guacamole: OMNIVORE'
Certains invités ont des allergies et d'autres restrictions alimentaires. Ces ingrédients doivent être étiquetés ou annotés pour chaque plat afin d'éviter tout problème.
Implémente la fonction tag_special_ingredients(<dish>), qui prend un tuple contenant le nom du plat en première position et une list ou un set d'ingrédients de ce plat en seconde position.
Renvoie le nom du plat suivi du set des ingrédients qui nécessitent une note particulière dans la description du plat.
Les ingrédients d'un plat dans une list peuvent contenir des doublons ou non.
Pour les besoins de cet exercice, tous les allergènes ou ingrédients spéciaux à étiqueter figurent dans la constante SPECIAL_INGREDIENTS importée depuis sets_categories_data.py.
>>> from sets_categories_data import SPECIAL_INGREDIENTS
>>> tag_special_ingredients(('Ginger Glazed Tofu Cutlets', ['tofu', 'soy sauce', 'ginger', 'corn starch', 'garlic', 'brown sugar', 'sesame seeds', 'lemon juice']))
...
('Ginger Glazed Tofu Cutlets', {'garlic','soy sauce','tofu'})
>>> tag_special_ingredients(('Arugula and Roasted Pork Salad', ['pork tenderloin', 'arugula', 'pears', 'blue cheese', 'pine nuts', 'balsamic vinegar', 'onions', 'black pepper']))
...
('Arugula and Roasted Pork Salad', {'pork tenderloin', 'blue cheese', 'pine nuts', 'onions'})
Pour préparer les commandes et les courses, tu devras compiler une « liste maîtresse » des ingrédients de tout ce qui figure au menu (quantités à compléter plus tard).
Implémente la fonction compile_ingredients(<dishes>), qui prend une list de plats et renvoie un set de tous les ingrédients de l'ensemble des plats listés.
Chaque plat est représenté par son set d'ingrédients.
dishes = [ {'tofu', 'soy sauce', 'ginger', 'corn starch', 'garlic', 'brown sugar', 'sesame seeds', 'lemon juice'},
{'pork tenderloin', 'arugula', 'pears', 'blue cheese', 'pine nuts',
'balsamic vinegar', 'onions', 'black pepper'},
{'honeydew', 'coconut water', 'mint leaves', 'lime juice', 'salt', 'english cucumber'}]
>>> compile_ingredients(dishes)
...
{'arugula', 'brown sugar', 'honeydew', 'coconut water', 'english cucumber', 'balsamic vinegar', 'mint leaves', 'pears', 'pork tenderloin', 'ginger', 'blue cheese', 'soy sauce', 'sesame seeds', 'black pepper', 'garlic', 'lime juice', 'corn starch', 'pine nuts', 'lemon juice', 'onions', 'salt', 'tofu'}
Les hôtes t'ont donné une liste de plats qu'ils aimeraient faire préparer en amuse-bouches, de la taille d'une bouchée, pour être servis sur des plateaux. Tu dois les retirer de la liste principale des plats préparés en portions plus généreuses.
Implémente la fonction separate_appetizers(<dishes>, <appetizers>), qui prend une list de noms de plats et une list de noms d'amuse-bouches.
La fonction doit renvoyer la list des noms de plats, sans les noms d'amuse-bouches.
La list <dishes> comme la list <appetizers> peut contenir des doublons et devra peut-être être dédupliquée.
dishes = ['Avocado Deviled Eggs','Flank Steak with Chimichurri and Asparagus', 'Kingfish Lettuce Cups',
'Grilled Flank Steak with Caesar Salad','Vegetarian Khoresh Bademjan','Avocado Deviled Eggs',
'Barley Risotto','Kingfish Lettuce Cups']
appetizers = ['Kingfish Lettuce Cups','Avocado Deviled Eggs','Satay Steak Skewers',
'Dahi Puri with Black Chickpeas','Avocado Deviled Eggs','Asparagus Puffs',
'Asparagus Puffs']
>>> separate_appetizers(dishes, appetizers)
...
['Vegetarian Khoresh Bademjan', 'Barley Risotto', 'Flank Steak with Chimichurri and Asparagus',
'Grilled Flank Steak with Caesar Salad']
Dans chaque catégorie (Vegan, Vegetarian, Paleo, Keto, Omnivore), tu vas extraire les ingrédients qui n'apparaissent que dans un seul plat. Ces ingrédients « singletons » seront confiés à un acheteur dédié afin qu'ils ne soient pas oubliés dans la précipitation à tout préparer.
Implémente la fonction singleton_ingredients(<dishes>, <INTERSECTIONS>), qui prend une list de plats et une constante <CATEGORY>_INTERSECTIONS pour la même catégorie.
Chaque plat est représenté par un set de ses ingrédients.
Chaque <CATEGORY>_INTERSECTIONS est un set d'ingrédients qui apparaissent dans plusieurs plats de la catégorie.
À l'aide d'opérations sur les ensembles, ta fonction doit renvoyer un set d'ingrédients « singletons » (des ingrédients qui n'apparaissent que dans un seul plat de la catégorie).
from sets_categories_data import example_dishes, EXAMPLE_INTERSECTION
>>> singleton_ingredients(example_dishes, EXAMPLE_INTERSECTION)
...
{'garlic powder', 'sunflower oil', 'mixed herbs', 'cornstarch', 'celeriac', 'honey', 'mushrooms', 'bell pepper', 'rosemary', 'parsley', 'lemon', 'yeast', 'vegetable oil', 'vegetable stock', 'silken tofu', 'tofu', 'cashews', 'lemon zest', 'smoked tofu', 'spaghetti', 'ginger', 'breadcrumbs', 'tomatoes', 'barley malt', 'red pepper flakes', 'oregano', 'red onion', 'fresh basil'}
Inscris-toi sur Exercism pour apprendre et maîtriser Python avec 17 concepts146 exercices, et un vrai mentorat humain, le tout gratuitement.