Complète TP sur les fonctions + cours

This commit is contained in:
Alexis Fourmaux 2025-09-11 20:55:31 +02:00
parent f6d545059f
commit 51d6511426
4 changed files with 287 additions and 479 deletions

View file

@ -6,141 +6,197 @@
- **Fonctions** : Création de fonctions
- **Refactorisation** : réorganiser le code existant
- **Modules** : Import de modules externes
- **Modules** : Répartir le code dans différents fichiers
### Objectif technique
### Objectifs techniques
Créer un menu interactif pour que l'utilisateurice puisse choisir son option
```
================================================
CIEL - Générateur de mots de passe
================================================
Quel type de mot de passe souhaitez-vous créer ?
1 - Mot de passe avec configuration par défaut
2 - Mot de passe avec configuration personnalisée
3 - Phrase de passe avec configuration par défaut
4 - Phrase de passe avec configuration personnalisée
0 - Quitter
Saisissez votre choix : 1
Mot de passe par défaut
```
- Générer un mot de passe
- Réorganiser le code en créant un module pour générer les mots de passe
## Étapes
### 1 - Préparation
### 1 - Fonction `afficher_menu`
**Objectif :** Préparer votre fichier de script et commencer à travailler avec Thonny
**Objectif :** Créer une fonction `afficher_menu` qui affiche le menu.
```admonish travail
- Créer un fichier `main.py` dans votre répertoire de travail
- Créer une fonction `afficher_menu` qui ne prend aucun paramètre.
- Extraire dans cette fonction tout le code permettant d'**afficher** votre menu
- Remplacer le code d'affichage par un appel à votre nouvelle fonction
```
### 2 - Affichage du titre et du menu
**Objectif :** Afficher le titre et toutes les options du menu
```admonish tip title="A utiliser"
- La fonction `print`
```admonish warning title="Attention"
On garde la variable `choix` et le `input` dans le corps du script. Cette ligne ne change pas de place.
```
**Résultat attendu :**
```
================================================
CIEL - Générateur de mots de passe
================================================
Quel type de mot de passe souhaitez-vous créer ?
1 - Mot de passe avec configuration par défaut
2 - Mot de passe avec configuration personnalisée
3 - Phrase de passe avec configuration par défaut
4 - Phrase de passe avec configuration personnalisée
0 - Quitter
```admonish note title="Dans le compte rendu"
1. Donner le code de la fonction
1. Où doit être déclarée la fonction ?
1. Comment tester que la fonction marche ?
```
### 2 - Fonction `choix_generateur`
**Objectif :** Créer une fonction qui reçoit le choix de l'utilisateur et le traite
```admonish travail
- Utilisez du code pour afficher exactement le texte ci-dessus
- Créer une fonction `choix_generateur` qui prend en paramètre le choix de l'utilisateur.
~~~python
def choix_utilisateur(choix):
# Ecrire le code ici
~~~
- Extraire dans cette fonction tout le code permettant de gérer vos choix et afficher la ligne correspondante
- Remplacer votre ancien code par un appel à la nouvelle fonction. N'oubliez pas de passer votre variable en paramètre
```
### 3 - Demander le choix à l'utilisateur
```admonish warning title="Attention"
Si vous avez utilisé `break` dans votre structure conditionnelle, cela ne fonctionnera plus : il se trouve dans une sous-fonction, et donc il n'est plus directement dans la boucle.
**Objectif :** Afficher "Saisissez votre choix : " et récupérer la réponse de l'utilisateur
```admonish tip title="A utiliser"
- La fonction `input`
Il faudra donc gérer la pause avec une condition sur `choix`.
```
```admonish question
1. Quel est le type de la donnée récupérée via `input`
2. Comment allez-vous stocker cette donnée ? Quel type aura la variable ?
```admonish note title="Dans le compte rendu"
1. Donner le code de la fonction
1. Donner le nouveau code de votre boucle principale
1. Pourquoi passer la varaible choix en paramètre au lieu d'une variable globale ?
1. Si je modifie choix dans ma fonction, qu'en est-il de la variable choix originale (celle à l'extérieur de la fonction) ?
```
### 4 - Gérer les choix valides
### 3 - Fonction `generer_mdp`
**Objectif :** Utiliser des conditions pour gérer les choix de l'utilisateur (1, 2, 3, 4 et 0)
**Objectif :** Créer une fonction pour générer un mot de passe (fixe pour le moment)
```admonish tip title="A utiliser"
- `if`
- `elif`
- Opérateurs de comparaison
```admonish travail
1. Créer une fonction `generer_mdp` qui retourne simplement la chaîne de caractères `P@ssw0rd!`. Penser à utiliser l'instruction `return`
1. Utiliser cette fonction dans le choix 1 pour afficher le mot de passe généré dans la console.
```
**Résultat attendu :** Afficher le texte pour chacun des cas
|Choix|Texte à afficher|
|-|-|
|1|Mot de passe par défaut|
|2|Mot de passe personnalisé|
|3|Phrase de passe par défaut|
|4|Phrase de passe personnalisée|
|0|Vous quittez le programme|
### 5 - Gérer les choix invalides
**Objectif :** Traiter le cas où l'utilisateur saisit un choix non valide
```admonish tip title="A utiliser"
- Ajouter un `else` pour capturer les autres cas
- Utiliser une chaîne formatée
```admonish note title="Dans le compte rendu"
1. Donner le code de la nouvelle fonction créée
1. Donner le code de `choix_generateur` après l'avoir modifiée
1. Est-ce une bonne idée d'utiliser `P@ssw0rd!` comme vrai mot de passe pour un de vos comptes en ligne ? Pourquoi ?
```
**Résultat attendu :** Afficher le texte `Erreur : l'option 6 n'existe pas` (si le choix de l'utilisateur est 6 par exemple)
### 4 - Création d'un module `mdp`
```admonish question
1. Que se passe-t-il lorsque l'utilisateurice saisit une lettre au lieu d'un chiffre lors du choix de l'option ? Pourquoi à votre avis ? Nous ignorerons cette possibilité pour le moment, et verrons plus tard dans l'année comment le gérer.
**Objectif :** Créer un module `mdp` pour y mettre tout ce qui concerne la génération de mots de passe, et l'importer dans le code principal
```admonish travail
1. Créer un nouveau fichier `mdp.py`
1. Déplacer la fonction `generer_mdp` dans ce nouveau fichier
1. Importer le module mdp dans le fichier principal
1. Modifier l'appel à la fonction `generer_mdp` pour tenir compte de son déplacement dans un module
1. Vérifier que tout fonctionne comme avant
```
### 6 - Ajouter une boucle
```admonish help title="Aide"
- Pour importer le module `mdp` il faut utiliser l'instruction `import`
- Pour utiliser la fonction `generer_mdp` du module `mdp`, il faut préciser le nom du module : `mdp.generer_mdp()`
~~~python
import mdp
**Objectif :** Le programme doit pouvoir être répété autant que l'utilisateur le veut.
... # Code du script
```admonish tip title="Quelle boucle utiliser ?"
- La boucle `for` s'utilise pour un nombre connu d'itérations
- La boucle `while` s'utilise pour un nombre non défini à l'avance d'itérations
mdp.generer_mdp()
... # Encore du code
~~~
```
```admonish warning title="Gestion de la pause"
Si le choix de l'utilisateurice est différent de zéro (pour quitter), il faut avoir le temps de lire avant de réafficher le menu.
Pour cela on peut demander une entrée (avec `input`) à l'utilisateurice, mais ignorer sa saisie. On peut alors simplement lui proposer d'appuyer sur Entrée. Une fois la saisie effectuée, on pourra alors reboucler.
```admonish warning title="Attention"
Ne pas oublier de préciser le nom du module avec un point lorsqu'on appelle une fonction qui est dans un module externe (`mon_module.ma_fonction()`)
```
```admonish question
1. Quelle boucle sera la plus adaptée ? Pourquoi ?
2. Comment éviter de déclencher la pause lorsque l'utilisateurice souhaite quitter le programme ?
```admonish note title="Dans le compte rendu"
- Mettre le code du nouveau module
- Mettre la ligne modifiée pour appeler `generer_mdp` depuis le script principal
- Quel est l'intérêt de créer des modules séparés pour certaines portions de code ?
```
### 5 - Modification de `generer_mdp` pour générer un vrai mot de passe
**Objectif :** Générer un mot de passe réellement aléatoire de taille fixe
```admonish travail
1. Dans le module `mdp`, tout en haut du fichier, importer les modules `secrets` et `string` qui sont livrés avec Python
1. En haut du fichier, après les imports, créer quatre **constantes** comme ci-dessous. Elles vont nous aider à définir l'ensemble des caractères autorisés dans les mots de passe
~~~python
LETTRES = string.ascii_letters # Définit toutes les lettres autorisées
CHIFFRES = string.digits # Définit tous les chiffres autorisés
SPECIAL = string.punctuation # Définit les caractères spéciaux autorisés
ALPHABET = LETTRES + CHIFFRES + SPECIAL # Ensemble complet des caractères autorisés
~~~
1. Modifier la fonction `generer_mdp` pour générer un mot de passe de 14 caractères. Pour cela :
1. Ajouter une boucle qui bouclera 14 fois, grâce à la fonction `range`
2. Dans cette boucle, nous allons ajouter un nouveau caractère à la variable `password`. Ce caractère sera tiré aléatoirement dans la constante `ALPHABET`.
Pour ça, nous allons utiliser la fonction `choice` du module `secrets` :
~~~python
password += secrets.choice(ALPHABET)
~~~
3. A la fin, une fois sorti de la boucle, nous retournons la variable `password`
1. Vérifier que votre fonction marche en essayant de générer un mot de passe (via l'option 1)
```
```admonish tip title="Remarques"
1. Les constantes en Python sont comme des variables. La différence est qu'on ne voudra jamais changer leur valeur. Mais rien ne permet de l'empêcher. Alors pour savoir qu'il s'agit d'une constante et qu'elle ne devrait pas être modifiée, on lui donne un nom en majuscules. Il s'agit d'une convention courante en programmation.
1. Les constantes `string.ascii_letters`, `string.digits`, `string.punctuation` sont des chaînes de caractères avec l'ensemble des caractères de chaque catégorie. Elles sont définies dans le module string pour nous faire gagner du temps et éviter d'en oublier.
1. Pour ajouter un caractère à une chaîne, il suffit d'utiliser `+`
~~~python
ma_chaine = ma_chaine + "c"
~~~
```
```admonish warning title="Attention"
- Ne pas oublier d'initialiser la variable `password` **avant la boucle** avec une chaîne de caractères vide : `""`
- Appeler l'instruction `return` **après être sorti de la boucle**
```
```admonish note title="Dans le compte rendu"
1. Donner le nouveau code du module `mdp` **complet**
1. Aller voir la [documentation du module `random` de python](https://docs.python.org/3/library/random.html).
1. Expliquer en quelques mots le rôle de la bibliothèque `random`
1. Il y a un avertissement important sur la page, dans l'introduction. À la lecture de cet avertissement, expliquer en quelques mots pourquoi nous avons choisi la bibliothèque `secrets` plutôt que `random` pour générer un mot de passe **sécurisé**.
```
### 6 - Réutiliser `generer_mdp` pour créer un mot de passe de taille personnalisable
**Objectif :** Rendre personnalisable la taille du mot de passe généré, et compléter l'option 2 du menu
```admonish travail
1. Modifier la fonction `generer_mdp` pour qu'elle puisse prendre un paramètre `password_length`. Ce paramètre aura une **valeur par défaut** : 14
1. Modifier la boucle pour qu'elle puisse faire autant d'itérations que `password_length` le nécessite.
1. Vérifier que le code fonctionne toujours normalement sans autre modification
1. Modifier le choix 2 du menu pour :
1. Demander à l'utilisateurice de saisir une longueur de mot de passe peronnalisée (avec `input`)
1. Utiliser la fonction `generer_mdp` pour afficher un mot de passe aléatoire de la longueur demandée par l'utilisateurice
1. Vérifier que le choix 2 vous permette de générer des mots de passe de taille choisie.
```
```admonish note title="Dans le compte rendu"
1. Donner le code modifié de la fonction `generer_mdp`
2. Donner le code modifié du choix 2 du menu
3. Expliquer pourquoi votre choix 1 fonctionne toujours alors que vous ne passez aucun paramètre à la fonction `generer_mdp`
```
## Validation
Livrez le code final en plus du compte-rendu du TP
### Checklist
- [ ] Le programme se lance sans erreur
- [ ] Le titre s'affiche correctement
- [ ] Toutes les options s'affichent
- [ ] La saisie utilisateur fonctionne
- [ ] Chaque option affcihe le bon message
- [ ] Un choix (nombre entier) invalide affiche une erreur claire
- [ ] Le code est propre, les variables sont correctement nommées et avec des noms compréhensibles
- [ ] La fonction `generer_mdp` est créée avec les bons paramètres
- [ ] Le module `mdp` est créé et contient les bonnes fonctions
- [ ] Le code principal utilise les fonctions du module `mdp`
- [ ] Les mots de passe générés avec l'option 1 comportent 14 caractères et sont aléatoires
- [ ] Les mots de passe générés avec l'option 2 sont de la taille choisie et aléatoires
- [ ] Les fonctions ont des docstrings minimales (avec explication de leur fonctionnalité)
- [ ] Le code est propre, les variables et fonctions sont correctement nommées et avec des noms compréhensibles et adaptés
### Critères d'évaluation
@ -150,29 +206,45 @@ Pour cela on peut demander une entrée (avec `input`) à l'utilisateurice, mais
- Réponse aux questions dans le compte-rendu
## Pour aller plus loin
### Bonus - Améliorations
- Personnaliser le titre avec votre nom
- Améliorer le texte des options et les messages
- Amélioration de la présentation du menu
### Bonus difficile - Utiliser l'instruction `match`
### Bonus facile - Ajout de docstrings
```admonish warning title='Attention'
Cette question n'est à faire **que si le reste du TP est terminé**. Elle demande plus d'autonomie et est beaucoup moins guidée.
Si vous n'êtes pas très à l'aise avec Python ou la programmation il vaut mieux se concentrer sur le reste et bien comprendre.
**Objectif**: Documenter votre code à l'aide de docstrings complètes et formatées au format Numpy
Cette section n'est pas nécessaire à la bonne exécution du TP ni des séances suivantes. On peut terminer sans la faire.
**Faites une copie de votre code dans un nouveau fichier avant toute modification afin de pouvoir revenir en arrière au prochain TP si vous ne terminez pas cette question.**
```admonish travail
1. À l'aide de la section "Bonnes pratiques" du cours, ajoutez des docstrings aux fonctions de votre TP pour les documenter en respectant la syntaxe Numpy fournie
```
**Objectif :** Utiliser la documentation Python pour remplacer les conditions du menu par la structure `match`.
```admonish tip title="A utiliser"
- Utiliser la documentation officielle de Python pour comprendre la structure `match` : [Documentation](https://docs.python.org/3/tutorial/controlflow.html#match-statements)
- Utiliser la structure match pour remplacer la suite de conditions
### Bonus difficile - Ajout de contraintes
**Objectif** : Créer une fonction `generer_mdp_avec_contraintes` pour ajouter des contraintes sur le nombre de caractères spéciaux et de chiffres
```admonish travail
1. Créer une nouvelle fonction `generer_mdp_avec_contraintes` dans le module `mdp`. Cette fonction appellera `generer_mdp` et retournera le mot de passe généré. Elle prendra trois paramètres :
- `password_length` pour la longueur du mot de passe. Cette valeur sera passée à `generer_mdp` telle quelle et aura 14 en valeur par défaut.
- `special` pour le nombre minimum de caractères spéciaux. Sa valeur par défaut sera de 1.
- `digits` pour le nombre minimum de chiffres. Sa valeur par défaut sera de 1.
2. Ajouter une boucle qui permettra de re-générer le mot de passe tant qu'il ne comportera pas au moins `special` caractères spéciaux et `digits` chiffres. Il faudra pour cela :
- Compter les caractères spéciaux (et les chiffres) dans votre mot de passe
- Vérifier ensuite que ce nombre soit supérieur au paramètre correspondant (`digits` ou `special`)
- Arrêter la boucle si ces deux conditions sont réunies (donc la continuer tant que les deux conditions ne sont pas réunies)
3. Remplacer la fonction `generer_mdp` dans l'option 2 du menu pour utiliser votre nouvelle fonction.
4. Vérfiez que les mots de passe générés respectent la contrainte par défaut
5. Sur le modèle de la longueur du mot de passe, demander à l'utilisateurice de choisir les contraintes voulues.
6. Documenter votre/vos nouvelles fonctions avec une docstring complète.
```
```admonish question
- Comment gérer les cas non prévus dans le menu (ce que faisait le `else`) avec `match` ?
```admonish help title="Aide"
1. Pour compter les caractères spéciaux (ou les chiffres) dans votre mot de passe, vous pouvez
- faire une boucle 'for' sur votre mot de passe
- vérifier si le caractère se trouve dans votre constante `SPECIAL` avec la syntaxe `caractere in SPECIAL`
- et incrémenter une variable à chaque fois que la condition précédente est à `True`
2. Vous pouvez extraire le comptage et la validation de la contrainte dans une sous-fonction pour rendre votre code plus lisible.
```
```admonish note title="Dans le compte rendu"
1. Donner le code de la fonction `generer_mdp_avec_contraintes` et des éventuelles sous-fonctions créées.
2. Donner le code modifié du choix 2 du menu
```