Exercice Python : Secret Santa
Retour à la liste des articles
04 décembre 2019
Une fois n'est pas coutume, regardons ce que dit Wikipédia à propos de Secret Santa :
Secret Santa, ou Noël canadien au Québec et dans les pays francophones, est une tradition de Noël, surtout dans les pays anglo-saxons, lors de laquelle les membres d'un groupe ou d'une communauté s'offrent au hasard des cadeaux. Le tirage au sort est anonyme (il peut y avoir des variantes, mais souvent le nom de l'offrant est révélé seulement le jour de l'échange des cadeaux).
Chaque participant-e va donc offrir un cadeau à une autre personne au hasard, pour qu'au final chacun-e offre un cadeau et en reçoive un autre.
Prérequis pour cet article
Assure-toi d'avoir les connaissances nécessaires pour cet article en terminant la lecture du chapitre 6 « Un programme dynamique avec Python ».
Tu ne possèdes pas encore Génies du code ? Il s'agit d'une méthode illustrée, adaptée à tous les niveaux, qui te fera découvrir la programmation à travers la réalisation de ton propre site web de A à Z. Les deux premiers chapitres sont disponibles gratuitement dans leur intégralité !
Découvrir Génies du codeLe but de cet exercice est de demander une liste de noms, puis de créer des paires personne qui donne - personne qui reçoit. Examinons les étapes nécessaires pour ce programme :
- Demander les noms à l'utilisateur-trice ;
- Créer une liste à partir de ces noms ;
- Mélanger la liste ;
- Créer des paires en associant chaque élément de la liste avec l'élément suivant.
Concrètement, en termes de types de données Python, on aimerait donc partir d'une chaîne de caractères, où chaque nom est séparé par une virgule, pour arriver à une liste de paires personne qui donne - personne qui reçoit :
# 1. Chaîne de caractères entrée par l'utilisateur-trice
'Cyril, Julie, Caipi'
# 2. Création d'une liste à partir de ces noms séparés par des virgules
['Cyril', 'Julie', 'Caipi']
# 3. Mélange de la liste
['Julie', 'Caipi', 'Cyril']
# 4. Transformation en liste de paires. Chaque élément est
# associé à l'élément qui le suit. Le dernier élément
# est associé au premier
[('Julie', 'Caipi'), ('Caipi', 'Cyril'), ('Cyril', 'Julie')]
Dans cet exemple, Julie va offrir un cadeau à Caipi, Caipi à Cyril, et Cyril à Julie.
Allez zou, retrousse tes manches, on va maintenant écrire le code pour chacune de ces étapes !
Création de la liste de noms
La première étape consiste à transformer une chaîne de caractères, par exemple
"Cyril,Julie,Caipi"
en liste Python, par exemple ['Cyril', 'Julie',
'Caipi']
. Complète la fonction split_names
ci-dessous pour qu'elle transforme une
chaîne de caractères, avec des noms séparés par des virgules, en une liste :
Le résultat des instructions print() s'affichera ici.
__check(split_names, ['Julie', 'Caipi', 'Cyril'], args=["Julie,Caipi,Cyril"])
Solution
def split_names(names):
return names.split(",")
print(split_names("Julie,Caipi,Cyril"))
Indice
Utilise la méthode split
pour créer une liste à partir d'une chaîne de caractères.
Tu remarqueras que si la personne met des espaces entre les virgules, par
exemple en entrant "Julie, Caipi, Cyril", les éléments dans la liste auront eux
aussi des espaces (['Julie', ' Caipi', ' Cyril']
), ce qui est peu élégant.
La méthode strip
permet d'éliminer les espaces au début et à la fin d'une
chaîne de caractères :
>>> ' Caipi'.strip()
'Caipi'
On pourrait l'utiliser sur chaque élément de la liste, par exemple de la façon suivante :
def split_names(names):
names_list = names.split(",")
names_list_stripped = []
for name in names_list:
names_list_stripped.append(name.strip())
return names_list_stripped
print(split_names("Julie, Cyril, Caipi"))
Ce serait correct, mais il existe une façon plus concise de l'écrire, qui évite
au passage de passer par une nouvelle variable intermédiaire
(names_list_stripped
). Il s'agit des compréhensions de listes.
Les compréhensions de listes
Les compréhensions de listes permettent de créer une nouvelle liste à partir
d'une liste existante, la plupart du temps en appliquant une fonction sur chaque
élément. Prenons par exemple le code suivant, qui crée une variable double
et
y met chaque élément de la liste my_list
multiplié par 2 :
Le résultat des instructions print() s'affichera ici.
On a d'abord dû créer une liste double
vide, puis parcourir la liste my_list
avec une boucle, et enfin ajouter chaque élément dans la nouvelle liste double
.
Une compréhension de liste permet de réaliser ces trois étapes en une seule instruction, ce qui rend le code nettement plus concis. La liste est directement créée avec les bons éléments dedans :
Le résultat des instructions print() s'affichera ici.
Une compréhension de liste utilise donc la même syntaxe for...in
, que tu as
déjà utilisée pour les boucles, mais à l'intérieur de crochets, pour indiquer
que cette instruction va initialiser une nouvelle liste.
Les compréhensions de listes peuvent aussi être utilisées pour filtrer les
éléments d'une liste en y ajoutant une instruction if
. Par exemple, la
compréhension de liste suivante crée une nouvelle liste qui ne contient que les
nombres supérieurs à 1 :
Le résultat des instructions print() s'affichera ici.
Les compréhensions de listes sont un peu difficiles à lire au début, mais une fois qu'on y est habitué-e, on a envie de les utiliser partout ! Je t'encourage à les utiliser autant que possible, cela rendra ton code plus concis, et plus facile à lire.
Exercice : compréhension de liste
Réécris la fonction split_names
pour utiliser une compréhension de liste pour
retirer les espaces au début et à la fin des noms. Profites-en pour ajouter de l'interactivité
en demandant la liste des noms à l'utilisateur-trice à l'aide de la fonction input
.
Le résultat des instructions print() s'affichera ici.
__check(split_names, ['Caipi', 'Cyril', 'Julie'], args=["Caipi, Cyril, Julie"])
Solution
def split_names(names):
return [name.strip() for name in names.split(",")]
print(split_names(input("Entre les noms des participant-e-s, séparés par des virgules: ")))
Mélanger la liste
On a maintenant une liste de noms, que l'on va devoir mélanger pour que le
résultat ne soit pas prévisible. Puisque l'on sait qu'on va devoir utiliser des
éléments aléatoires dans notre programme, regardons dans la documentation de
Python s'il n'y aurait pas à tout hasard un module qui pourrait nous aider. Quel
hasard ! Il se trouve qu'il existe justement un module
random
, qui met à
disposition une panoplie de fonctions en lien avec l'aléatoire. Ce module
dispose en particulier d'une fonction
shuffle
, qui
permet justement de mélanger une liste :
random.shuffle(x[, random])
Shuffle the sequence x in place. [...]
La description de la fonction mentionne in place, cela signifie qu'au lieu de renvoyer une liste modifiée, elle va modifier directement la liste passée en paramètre, et qu'on a donc pas besoin de passer par une valeur de retour.
Essaie d'exécuter le programme suivant plusieurs fois pour voir l'ordre des éléments dans la liste changer.
Le résultat des instructions print() s'affichera ici.
Exercice : mélanger la liste
Modifie la ligne 9 du code ci-dessous pour mélanger les éléments de la liste avant de les afficher.
Le résultat des instructions print() s'affichera ici.
Solution
import random
def split_names(names):
return [name.strip() for name in names.split(",")]
names = split_names(
input("Entre les noms des participant-e-s, séparés par des virgules: ")
)
random.shuffle(names)
print(names)
Maintenant que notre liste est mélangée, il ne reste plus qu'à créer des paires. L'échange de cadeaux se rapprochent dangereusement!
Créer des paires
Le but de cette dernière étape est de créer une fonction make_pairs
, qui prend
en paramètre une liste et qui crée des paires avec chaque élément de la liste et
son élément suivant (et le dernier élément pointe sur le premier). Puisque cette
phrase n'est pas du tout claire, je te propose de regarder plutôt par toi-même
ce que je veux dire :
>>> make_pairs(['Cyril', 'Julie', 'Caipi'])
[('Cyril', 'Julie'), ('Julie', 'Caipi'), ('Caipi', 'Cyril')]
Le résultat des instructions print() s'affichera ici.
__check(make_pairs, [('Cyril', 'Julie'), ('Julie', 'Caipi'), ('Caipi', 'Cyril')], args=[['Cyril', 'Julie', 'Caipi']])
Solution
def make_pairs(names):
names_pairs = []
for i, name in enumerate(names):
# name est l'élément courant de la liste
# names[i + 1] est l'élément suivant
# L'opérateur % permet de recommencer au début de la liste
# lorsqu'on se trouve sur le dernier élément
pair = (name, names[(i + 1) % len(names)])
names_pairs.append(pair)
return names_pairs
## La même chose, avec une compréhension de liste
#def make_pairs(names):
# return [(name, names[(i + 1) % len(names)]) for i, name in enumerate(names)]
print(make_pairs(['Cyril', 'Julie', 'Caipi']))
Indice
Utilise la fonction enumerate
pour obtenir des paires (indice, élément)
à partir de la liste
names
. Tu pourras alors accéder à l'élément suivant dans la liste en
accédant à names[indice + 1]
. Pour créer une paire composée du
dernier élément de la liste et du premier, utilise l'opérateur
%
(modulo).
Tu trouveras plus de détails sur l'opérateur modulo dans l'article Exercice Python : générateur de messages codés .
Créer le programme final
Regardons à nouveau les étapes nécessaires que l'on avait définies :
- Demander les noms à l'utilisateur-trice
-
Grâce à la fonction
input
;
-
Grâce à la fonction
- Créer une liste à partir de ces noms
-
Grâce à la fonction
split_names
;
-
Grâce à la fonction
- Mélanger la liste
-
Grâce à la fonction
random.shuffle
;
-
Grâce à la fonction
- Créer des paires en associant chaque élément de la liste avec l'élément suivant
-
Grâce à la fonction
make_pairs
.
-
Grâce à la fonction
On a maintenant toutes les fonctions nécessaires pour créer notre programme !
Complète le code suivant pour afficher "X va offrir un cadeau à Y" pour
chaque paire dans la liste renvoyée par make_pairs
.
Le résultat des instructions print() s'affichera ici.
Solution
import random
def make_pairs(names):
return [
(name, names[(i + 1) % len(names)])
for i, name in enumerate(names)
]
def split_names(names):
return [name.strip() for name in names.split(",")]
names = split_names(
input("Entre les noms des participant-e-s, séparés par des virgules: ")
)
random.shuffle(names)
pairs = make_pairs(names)
for giver, receiver in pairs:
print("{} va offrir un cadeau à {}".format(giver, receiver))
Indice
La variable pairs
est une liste qui contient des paires, par
exemple [('Julie', 'Caipi'), ('Caipi', 'Julie')]
. La syntaxe
for x, y in pairs:
permet d'extraire les éléments de chaque
paire. Ainsi, à chaque passage dans la boucle, x
correspondra au
premier élément de la paire, et y
au second élément.
Conclusion
Pour résoudre un problème, la meilleure approche est toujours de le diviser en problèmes plus petits et plus faciles à résoudre. Enfin en tout cas pour la programmation, dans la vie c’est moins sûr. Dans notre cas, cela consiste à créer des fonctions qui font des opérations très spécifiques, et qui, une fois mises ensemble, permettent de résoudre le problème initial.
Tu sais maintenant comment fonctionnent les compréhensions de liste. Utilise-les le plus possible pour t'habituer à leur syntaxe, je suis sûr que tu finiras par les aimer !
Tu trouveras ci-dessous la version complète du programme pour référence. N'hésite pas à l'utiliser pour tirer au sort les distributions de cadeaux, et à la partager avec tes ami-e-s.
Le résultat des instructions print() s'affichera ici.
P.S.
Si tu ne fêtes pas Noël, ou si tu as la chance d'avoir aboli l'échange de cadeaux dans ton foyer, ce programme peut s'avérer tout aussi utile pour surprendre ses potes en faisant une chaîne d'envoi de cartes postales. Tu peux aussi l'adapter pour, par exemple, tirer au sort la personne qui devra faire la vaisselle.
Sur ce, bonnes fêtes de fin d'année !
Tu veux en savoir plus ?
Génies du code est une méthode illustrée, adaptée à tous les niveaux, qui t'initiera à la programmation à travers la réalisation de ton propre site web de A à Z. Les deux premiers chapitres sont disponibles gratuitement dans leur intégralité !
Et aussi, fais un tour sur les autres articles, tous plus intéressants les uns que les autres, en toute modestie.