python lire

Identification

Infoforall

15 - Interface Console


Le disposif qui permet de faire interagir un humain et un système informatique se nomme une interface.

Nous avons déjà pas mal travaillé avec l'interface graphique Turtle :

Nous allons utiliser aujourd'hui l'interface textuelle Console et les deux fonctions natives que propose Python pour gérer cette interface :

  1. print() pour afficher sur l'écran et
  2. input() pour récupérer le texte tapé sur le clavier.

Nous allons également revoir la différence entre

  • fonctions d'interface et
  • fonctions gérant les données.

Logiciel nécessaire pour l'activité : Thonny

Résumé : Version HTML ou fond blanc ou ou PDF (couleur ou gris)

1 - Afficher à destination d'un humain sur la console

La fonction native print() permet d'afficher un message à destination d'un humain.

Pour forcer l'affichage des résultats mémorisés, il faut utiliser dans ce cas la fonction native print().

La fonction native print()

La fonction native print() renvoie toujours None. Son but ?

AFFICHER dans l'interface de sortie un message à destination d'un UTILISATEUR HUMAIN. Le système informatique ne pourra donc rien stocker en mémoire à priori : le message ne lui est pas destiné.

Retenez bien ceci :

  • print() ne renvoie rien !
  • N'utilisez jamais print() si on vous demande de renvoyer une réponse.
Savoir lire un enoncé
  • "Réaliser une fonction qui renvoie" signifie donc qu'il faut répondre via le mot-clé return
  • "Réaliser une fonction qui affiche" signifie donc qu'il faut utiliser la fonction native print()

01° On demande à quelqu'un de réaliser une fonction qui renvoie 10 fois l'argument qu'on lui transmet.

Quelle est la réponse correcte à fournir ? Quelle est la version qu'il ne fait surtout pas noter sur sa copie ?

1 2 3 4 5
def fois10A(entree): print(entree*10) def fois10B(entree): return entree*10

...CORRECTION...

La bonne réponse est la fonction fois10B puisqu'elle RENVOIE bien une réponse.

La fonction fois10A ne fait absolument pas le travail puisqu'elle ne fait qu'AFFICHER le résultat dans la console.

02° Utiliser le programme ci-dessous. Le programme provoque-t-il un affichage automatiquement ?

1 2 3 4
def fois10(entree): return entree*10 rep = fois10("A")

Visualiser dans la console le contenu de la variable rep. Pourra-t-on réutiliser le "résultat" plus tard ?

>>> rep 'AAAAAAAAAA' >>> type(rep) ?

...CORRECTION...

Pas d'affichage automatique de la réponse mais elle est stockée.

Puisque la réponse est stockée dans une variable, on pourra réutiliser cette chaîne plus tard.

03° Utiliser le programme ci-dessous. Trois questions :

  1. Le programme provoque-t-il un affichage automatiquement ?
  2. Visualiser dans la console le contenu de la variable rep. Pourra-t-on réutiliser le "résultat" plus tard ?
  3. Quelle est la confusion réalisée par la personne qui a écrit ce programme ?
1 2 3 4
def fois10(entree): print(entree*10) rep = fois10("A")
AAAAAAAAAA >>> rep >>> type(rep)

...CORRECTION...

Cette fois, c'est l'inverse : il y a un affichage automatique mais rien n'est stocké !

La fonction renvoie None et donc la variable ne contient ... rien.

Du coup, utiliser cette fonction de cette manière montre une profonde incompréhension de la différence entre

  • print() qui affiche mais ne renvoie rien
  • et return qui renvoie une réponse.

On remarquera l'absence des guillemets sur l'affichage précédent : c'est normal : il est destiné à un humain. Il ne s'agit pas d'afficher le contenu réel de la variable mais juste en fournir une représentation. L'humain doit juste voir le message. Savoir que c'est un string, un integer ou un float n'a finalement aucune importance pour lui.

04° On vous donne le programme suivant.

1 2 3 4
def addition(a, b): return a + b reponse = addition(5, 10)

Que contient reponse ?

Quel est l'énoncé d'exercice qui correspond à cette fonction ?

  1. Enoncé 1 : Créer une fonction qui renvoie la somme de a et de b
  2. Enoncé 2 : Créer une fonction qui affiche la somme de a et de b

...CORRECTION...

La variable reponse contient la réponse de la fonction, à savoir la somme de 5 et 10, donc 15.

L'énoncé correct est donc réaliser une fonction qui renvoie la somme.

05° On vous donne le code suivant.

1 2 3 4
def addition(a, b): print(a + b) reponse = addition(5, 10)

Que contient reponse ?

Quel est l'énoncé d'exercice qui correspond à cette fonction 

  1. Créer une fonction qui renvoie la somme de a et de b
  2. Créer une fonction qui affiche la somme de a et de b

...CORRECTION...

La variable reponse contient la réponse de la fonction, à savoir None !

C'est comme si on avait tapé ceci :

1 2 3 4 5
def addition(a, b): print(a + b) return None reponse = addition(5, 10)

L'énoncé correct est donc réaliser une fonction qui AFFICHE la somme. Il ne sert à rien ici de tenter de mémoriser la réponse : elle n'existe pas.

Fonctions d'interface machine-utilisateur

On nomme fonctions d'interface avec l'utilisateur les fonctions dont la tâche est d'interagir avec l'utilisateur à travers l'interface choisie : Turtle ou la console par exemple.

Si on respecte le concept "une tâche = une fonction", une fonction devra donc être

  • soit une fonction d'interface : elle peut provoquer un affichage ou récupérer la réponse de l'utilisateur
  • soit une fonction de gestion de données : elle réalise des calculs ou modifie les données et renvoie une réponse

Exemple basique 1 : fonction d'interface Console vers Utilisateur

1 2 3 4 5
def afficher(message): """Affiche le message sur l'interface Console""" print(message) afficher("Bonjour !")

L'exécution de ce programme provoque l'affichage suivant :

Bonjour !

Exemple basique 2 : fonction d'interface Turtle vers Utilisateur

1 2 3 4 5 6 7 8
import turtle as trt def afficher(message, feutre): """Affiche le message avec le feutre transmis""" feutre.write(message) crayon = trt.Turtle() afficher("Bonjour !", crayon)

L'utilisateur du programme précédent permet d'obtenir l'affichage suivant :

L'avantage des fonctions d'interface est donc qu'on n'a pas besoin de connaître les détails de l'interface sur laquelle on veut voir les choses s'afficher. De plus, si on change d'interface, il suffira de modifier le code interne des fonctions d'interface mais le reste du programme devra toujours être le même !

06° Bien entendu, on peut utiiser une fonction qui renvoie une réponse et afficher ENSUITE sa réponse via un programme : il faut simplement demander poliment.

1 2 3 4 5
def fois10(entree): return entree*10 rep = fois10("A") print(rep)

Sur quelle ligne force-t-on l'affichage de la réponse dans la console ?

...CORRECTION...

Sur la ligne 5 : celle avec la fonction native print().

07° Peut-on dire que la fonction fois10() précédente est une fonction d'interface ?

...CORRECTION...

Non ! C'est une fonction qui réalise une tâche qui n'a rien à voir avec la communication avec l'utilisateur.

C'est une fonction de gestion des données.

08°Quelqu'un propose de réaliser ceci. La fonction est opérationnelle, aucun problème. Mais quel est le problème ?

1 2 3 4
def fois10(entree): reponse = entree*10 print(reponse) return reponse

...CORRECTION...

Cette fonction n'est pas "acceptable" hors d'un prototype car elle est à la fois fonction d'interface et fonction de gestion de données.

  1. Une tâche consiste à réaliser un calcul sur les données et à fournir la réponse
  2. Une tâche consiste à afficher sur l'interface

On notera qu'il faut bien que certaines fonctions fassent à la fois partie des fonctions d'interface et des fonctions de gestion des données : il faut bien faire la liaison entre les deux parties. Mais il faut en limiter le nombre de ces fonctions de liaison.

La fonction native print() permet donc de créer une communication PROGRAMME vers HUMAIN en passant par l'INTERFACE.

Il est temps de revoir comment utiliser la fonction native input() permettant la communication HUMAIN vers PROGRAMME via l'INTERFACE console.

2 - Lire les entrées clavier

fonction native input()

Il s'agit de la fonction d'interface Console permettant la communication HUMAIN vers PROGRAMME.

Cette fonction réalise trois choses :

  1. Elle affiche sur la console le string qu'on lui passe en paramètre : cela permet de poser une question.
  2. Elle bloque le programme et enregistre ce qui est tapé sur le clavier TANT QUE l'utilisateur n'appuie pas sur la touche ENTREE
  3. Elle répond en renvoyant au programme sous forme d'un string ce que l'utilisateur vient de taper sur le clavier.

Voici l'exemple typique :

1 2 3 4
print("Début du programme") nom = input("Quel est votre nom ? ") print(f"Bonjour {nom}")

ATTENTION : la réponse de la fonction native input() est toujours un string. Si vous voulez récupérer un entier, il faut réaliser la conversion (ici en ligne 4) :

1 2 3 4 5 6 7
nom = input("Quel est votre nom ? ") age = input("Quel est votre age ? ") age = int(age) print(f"Bonjour {nom}") print(f"Vous avez {age*2} ans !")

09° Faire des essais dans la console interactive Python de façon à pouvoir voir le contenu de la réponse :

>>> input("Quel est votre prénom ? ")

Questions

  1. Que fait l'interpréteur Python en attendant la réponse de l'utilisateur ?
  2. Que renvoie la fonction input() ?
  3. Pourquoi obtient-on un affichage ?
  4. A quoi voit-on que la réponse de l'utilisateur est toujours fournie sous forme d'un string ?

On pourrait alors taper quelque chose. Ci-desssous, le résultat si on tape Alice puis ENTREE.

>>> input("Quel est votre prénom ? ") Quel est votre prénom ? Alice 'Alice'

...CORRECTION...

L'interpréteur est bloqué en attendant la réponse de l'utilisateur. La fonction input() ne permet donc pas de faire de la programmation événementielle : il faut suivre le déroulement du programme dans l'ordre imposé par le code.

La console affiche la réponse car nous sommes en mode interactif et qu'on ne fait rien de la réponse.

On remarque que la fonction native input() renvoie ce qu'on vient de taper sous forme d'un string : la réponse sur la console est entourée de guillemets.

10° Que va contenir la variable reponse si l'utisateur tape Bob+ENTREE sur le clavier ? Vérifier en évaluant la variable dans la console.

>>> reponse = input("Quel est votre prénom ? ") Quel est votre prénom ? Bob

...CORRECTION...

La variable reponse va contenir la réponse de la fonction native input, à savoir un string 'Bob'.

>>> reponse = input("Quel est votre prénom ? ") Quel est votre prénom ? Bob >>> reponse 'Bob'

11° Sans lancer réellement le programme, dire ce que devrait afficher Python si on dit qu'on a 5 à gauche et 10 à droite ?

1 2 3 4 5 6
gauche = input("Combien d'argent dans la poche Gauche ? ") droite = input("Combien d'argent dans la poche Droite ? ") total = gauche + droite print(f"Au total, vous avez {total} euros.")
Combien d'argent dans la poche Gauche ? 5 Combien d'argent dans la poche Droite ? 10 Au total, vous avez ??? euros.

...CORRECTION...

Combien d'argent dans la poche Gauche ? 5 Combien d'argent dans la poche Droite ? 10 Au total, vous avez 510 euros.
Rappel : concaténation

On retrouve l'"addition" de strings, ce qu'on nomme la concaténation.

Puisque les réponses récupérées par input() sont des strings, on concaténe le string '5' et le string '10', ce qui donne '510' :

>>> '5' + '10' '510' >>> 'Bon' + 'bon' 'Bonbon' >>> 5 + 10 15

Sur le dernier exemple, on voit que l'addition des deux entiers donne bien un entier.

Si on veut parvenir à additionner, il faut donc parvenir à convertir des strings en entiers. Pour cela, il faut simplement utiliser la fonction native int() qui tente de convertir si c'est possible. Attention, si l'utilisateur tape quelque chose de non interprétable en entier, ça provoquera une erreur.

12° Lancer ce nouveau programme qui demande des choses à l'utilisateur ET convertit les réponses en entier. Est-ce que cela marche mieux ? Sur quelle(s) ligne(s) convertit-on les réponses en entiers ?

1 2 3 4 5 6 7 8 9
gauche = input("Combien d'argent dans la poche Gauche ? ") gauche = int(gauche) droite = input("Combien d'argent dans la poche Droite ? ") droite = int(droite) total = gauche + droite print(f"Au total, vous avez {total} euros.")
Combien d'argent dans la poche Gauche ? 5 Combien d'argent dans la poche Droite ? 10 Au total, vous avez ??? euros.

...CORRECTION...

Combien d'argent dans la poche Gauche ? 5 Combien d'argent dans la poche Droite ? 10 Au total, vous avez 15 euros.

C'est beaucoup mieux. La conversion se fait sur les lignes 2 et 5.

13° Modifier le programme pour qu'il sache gérer les centimes et donc les nombres à virgule.

...CORRECTION...

Il suffit d'utiliser la fonction de conversion float plutôt que la fonction de conversion int.

1 2 3 4 5 6 7 8 9
gauche = input("Combien d'argent dans la poche Gauche ? ") gauche = float(gauche) droite = input("Combien d'argent dans la poche Droite ? ") droite = float(droite) total = gauche + droite print(f"Au total, vous avez {total} euros.")

Jusqu'à présent, nous avons utilisé directement la fonction native input().

Voyons comment nous pourrions l'encapsuler dans une fonction d'interface proprement si notre programme rentre dans le cadre d'un projet plus vaste.

3 - Les étapes pour réaliser un projet

Partie destinée à être lue et commentée en classe. Vous pouvez passer à la partie suivante pour le moment.

Je vous propose ci-dessous une méthode de création de programme en 8 étapes, sur le principe d'un micro-projet : récupérer deux nombres provenant de l'utilisateur et en faire la somme... C'est juste pour avoir un exemple simple et court.

Etape 1 / 8 - Réflechir de façon globale à l'algorithme sans trop rentrer dans les détails

On se demande comment faire de façon globale, sans trop rentrer dans les détails, et on trouve des noms explicites pour les premières variables ou fonctions qui viennent en tête.

  1. On récupère deux nombres gauche et droite fournis par l'utilisateur (via l'interface)
  2. On additionne les deux nombres et on stocke dans une variable total.
  3. On affiche total (via l'interface)

Etape 2 / 8 - Lister les tâches nécessaires

Si on liste les tâches à effectuer, on part sur ces fonctions à faire (sur le principe une tâche - une fonction) :

  1. Une fonction d'interface recuperer_un_entier() qui renvoie la valeur entiere transmise par l'utilisateur après lui avoir posé une question.
  2. Une fonction de gestion de données trouver_le_total() qui renvoie la somme de deux entiers.
  3. Une fonction d'interface afficher() qui permet d'afficher un résultat destiné à l'utilisateur humain.

Etape 3 / 8 - Prototyper les fonctions nécessaires

Il s'agit juste de choisir clairement les noms et les types des paramètres et le type de la réponse des fonctions. On utilise souvent les indications de type en les notant comme ici en vert par exemple :

  1. def recuperer_un_entier(question:str) -> int
  2. def trouver_le_total(a:int, b:int) -> int
  3. def afficher(message:str) -> None

On indique donc clairement sur papier que la fonction recuperer_un_entier() attend un seul paramètre de type string et qu'elle renvoie une réponse de type integer... C'est plus concis que de noter des phrases.

Etape 4 / 8 - Incorporer les fonctions en incorporant une documentation, exemples et fausse réponse temporaire

Pour les fonctions gestion de données, je ne place ici qu'un seul exemple lorsque c'est possible mais on en placera un peu plus que cela par la suite.

On rajoute également souvent le programme représentant l'algorithme en fin de script.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
# Partie déclaration des fonctions d'interface def recuperer_un_entier(question): """Récupère et renvoie un entier fourni par l'utilisateur""" return 0 def afficher(message): """Transmet un message à l'utilisateur""" pass # Partie déclaration des fonctions gérant les données def additionner(a, b): """Renvoie la somme des deux entiers :: param a(int) :: un entier :: param b(int) :: un entier :: return (int) :: la somme de a et b :: exemple .. >>> additionner(5, 10) 15 """ return 0 # Programme gauche = recuperer_un_entier("Combien d'argent dans la poche Gauche ? ") droite = recuperer_un_entier("Combien d'argent dans la poche Droite ? ") total = additionner(gauche, droite) afficher(f"Au total, vous avez {total} euros.")

Notez bien que pour chaque fonction, je renvoie une fausse réponse qui a au moins le bon type, ou pass lorsque la fonction ne doit rien répondre.

Etape 5 / 8 - Réaliser progressivement les fonctions et les tester au fur et à mesure

A partir d'ici, les différentes personnes travaillant sur le projet peuvent gèrer chacune leurs propres fonctions et on regroupe au fur et à mesure les fonctions dans un fichier commun.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
# Partie déclaration des fonctions d'interface def recuperer_un_entier(question): """Récupère et renvoie un entier fourni par l'utilisateur""" reponse = int(input(question)) return reponse def afficher(message): """Transmet un message à l'utilisateur""" print(message) # Partie déclaration des fonctions gérant les données def additionner(a, b): """Renvoie la somme des deux entiers :: param a(int) :: un entier :: param b(int) :: un entier :: return (int) :: la somme de a et b :: exemple .. >>> additionner(5, 10) 15 """ return a + b # Programme gauche = recuperer_un_entier("Combien d'argent dans la poche Gauche ? ") droite = recuperer_un_entier("Combien d'argent dans la poche Droite ? ") total = additionner(gauche, droite) afficher(f"Au total, vous avez {total} euros.")

14° Tester le programme ci-dessus pour vérifier qu'il fonctionne correctement.

La séparation Interface / Données peut paraître un peu artificiel sur un projet de 10 lignes, mais je vous assure qu'avec 30000 lignes, on est heureux d'avoir séparer gestion de l'interface et gestion des données !

Etape optionnelle 6 / 8 - Incorporer le programme principal dans une fonction de liaison

C'est cette fonction de liaison qui fera la liaison entre les fonctions d'interface et les fonctions qui gèrent les données.

15° Voici une dernière version du programme : on a créé une fonction jeu() qui contient les anciennes instructions du programme principal. Il s'agit donc d'une fonction de liaison puisqu'elle fait appel aux deux types de fonctions.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
# Partie déclaration des fonctions d'interface def recuperer_un_entier(question): """Récupère et renvoie un entier fourni par l'utilisateur""" reponse = int(input(question)) return reponse def afficher(message): """Transmet un message à l'utilisateur""" print(message) # Partie déclaration des fonctions gérant les données def additionner(a, b): """Renvoie la somme des deux entiers :: param a(int) :: un entier :: param b(int) :: un entier :: return (int) :: la somme de a et b :: exemple .. >>> additionner(5, 10) 15 """ return a + b # Partie déclaration des fonctions de liaison def jeu(): """Active les actions à réaliser""" gauche = recuperer_un_entier("Combien d'argent dans la poche Gauche ? ") droite = recuperer_un_entier("Combien d'argent dans la poche Droite ? ") total = additionner(gauche, droite) afficher(f"Au total, vous avez {total} euros.") # Programme jeu()

Etape 7 / 8 - Faire tester le programme par un testeur extérieur

On donne le programme à quelqu'un et on lui demande de l'utiliser en respectant les informations visibles dans la documentation. Il respecte donc les types des données à envoyer ect... Il vous rend sa copie en vous lisant les erreurs qu'il a rencontré en vous fournissant l'ensemble des instructions et des messages console qu'il a reçu.

Etape 8 / 8 - Modifier le programme en fonction des remarques du testeur

On modifie le programme et on peut éventuellement repartir sur l'étape 7 avec un nouveau testeur.

Vous allez devoir réaliser votre propre jeu dans les heures qui viennent. Il faudra donc appliquer la méthode proposée pour réaliser votre projet et parvenir au final à bien différencier

  • Les fonctions de gestion de données
  • Les fonctions d'interface
  • Les fonctions de liaison entre données et interface

Un autre exemple un peu plus compliqué

Voici par un autre exemple de découpe d'un programme en différentes fonctions. On réalise un jeu où on demande à l'utilisateur de trouver le résultat d'additions aléatoires et qui compte les points.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
# Importation import random as rd # Fonctions d'interface def recuperer_entier(message): """Renvoie l'entier fourni par l'utilisateur""" return int(input(message)) def afficher(message): print(message) # Fonctions de gestion de données def question(n1, n2): """Renvoie la question à afficher""" return f"Que vaut {n1} + {n2} ? " def resultat(reponse): """Renvoie le string à afficher en fonction du booléen reponse""" if reponse: return f"Bravo !" return "Votre réponse est fausse" def final(nbr_b, nbr_f): """Renvoie le message de fin""" return f"Vous avez eu {nbr_b} bonnes réponses sur {nbr_b + nbr_f}" def verifier(n, n1, n2): """Renvoie True si la réponse est bonne""" return n == (n1 + n2) def aleatoire(): return rd.randint(1,10) # Fonctions de liaison def jouer(): """On joue tant que l'utilisateur n'a pas fourni 5 bonnes réponses""" nbr_bonnes = 0 nbr_fausses = 0 while nbr_bonnes < 5: n1 = aleatoire() n2 = aleatoire() n = recuperer_entier(question(n1,n2)) bon = verifier(n, n1, n2) if bon: nbr_bonnes = nbr_bonnes + 1 else: nbr_fausses = nbr_fausses + 1 afficher(resultat(bon)) afficher(final(nbr_bonnes, nbr_fausses)) # Programme jouer()

16° Utiliser cette version du jeu en jouant une partie ou deux. Vérifier que vous comprennez le cheminement du programme en lui-même.

4 - Projet Console

Il faudra vous mettre en groupe de 2 à 3 personnes, choisir un thème de projet et suivre la méthode de travail suivante pour réaliser l'un des projets proposés (ou en proposer un et demander sa validation).

  • Etape 1 / 8 - Algorithme global sans trop rentrer dans les détails
  • Etape 2 / 8 - Lister les tâches/fonctions nécessaires
  • Etape 3 / 8 - Prototyper les fonctions nécessaires
  • Etape 4 / 8 - Incorporer les fonctions en incorporant une documentation, exemples et une fausse réponse
  • Etape 5 / 8 - Réaliser les fonctions et les tester au fur et à mesure
  • Etape 6 / 8 - Incorporer le programme principal dans une fonction de liaison données-interface
  • Etape 7 / 8 - Faire tester le programme par un testeur extérieur
  • Etape 8 / 8 - Modifier le programme en fonction des remarques du testeur

S'agissant cette fois d'un vrai projet, inutile de réflechir à tout dès le départ. Il est très important d'aboutir rapidement à un prototype : une version fonctionnelle mais ne comportant pas du tout toutes les fonctionnalités. Et à partir de là, on améliore peu à peu le projet.

Lors de la réalisation de l'étape 5, il convient donc de vous mettre d'accord avec les personnes de votre équipe pour savoir quelles sont les fonctionnalités les plus fondamentales à mettre en place rapidement.

La notation est décomposée en quatre parties notée sur 4 points :

  1. Le compte-rendu de projet : une feuille décrivant phase par phase et au jour le jour ce qui a été fait et par qui. Le compte-rendu peut contenir des informations liées à des directions et des choix abandonnés (car ils se sont avérés mauvais en réalité par exemple). Il n'y a que ceux qui ne vont rien qui ne se trompent jamais... Le compte-rendu commun est évalué de manière à estimer :
    1. la participation de tous (plutôt que réalisation d'un seul ou une seule élève) (on doit pouvoir savoir qui a fait quoi),
    2. la progression régulière (plutôt qu'une livraison totale juste avant la date limite) (on doit pouvoir dater la réalisation d'une partie ou d'une fonction),
    3. la recherche documentaire autonome en cas de problème (plutôt que de poser immédiatement une question à l'enseignant pour résoudre le problème) : le compte-rendu peut préciser une URL permettant de trouver les informations voulus...
    4. le soin et à la clarté des informations présentes sur le compte-rendu.
  2. Le respect des bonnes pratiques de programmation :
    • Noms explicites pour les variables, constantes et fonctions
    • Décomposition du programme en plusieurs parties (importations, déclarations des constantes,...)
    • Respect du principe "une tâche - une fonction"
    • Documentation des fonctions (pour savoir comment les utiliser)
    • Commentaires pour comprendre les parties compliquées des fonctions (pour savoir les modifier au besoin)
    • Repartition claire des fonctions en trois catégories (interface, données et liaison)
  3. La qualité de la réalisation en elle-même : finie ou pas, fonctionnelle ou pas, rajouts d'options ou pas...
  4. Une courte interrogation écrite sur votre production

Chaque partie est notée sur 5 MAIS vous ne pouvez pas avoir plus que la plus basse partie + 2.

Exemple 1 : un élève obtient 5-4-3-2. Il a eu un 2, les notes sont donc limitées à 4. Il obtient 4-4-3-2, soit 13/20.

Exemple 1 : un élève obtient 5-4-4-0. Il a eu un 0, les notes sont donc limitées à 2. Il obtient 2-2-2-0, soit 06/20.

Propostion 1 de projet : un QCM ?

  • Etape 1 : Au début, on demande à l'utilisateur de choisir un thème et un niveau de difficulté
  • Tant qu'il n'a pas le niveau requis de bonnes réponses (à voir en fonction du niveau de difficulté)
  • Etape 2 : On lui pose alors 5 questions tirées au hasard sur le thème
  • Etape 3 : On lui fait un récapitulatif des questions posées, des réponses fournies et on affiche les bonnes réponses lorsque la réponse fournie n'était pas la bonne.
  • On stockera les informations sur les questions d'un thème dans des tableaux :
    • Un tableau pour les questions (une question par case)
    • 4 tableaux pour les propositions 1 à 4 du QCM (les propositions de la question i seront donc la case i également)
    • Un tableau pour la bonne réponse attendue (la bonne réponse de la question i est bien entendi dans la case i)
    • Un tableau pour la réponse proposée par l'utilisateur (repérée par l'indice i)

    ...EXEMPLE...

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
    # Création de quelques tableaux pour stocker les données NBR = 20 # Nombre de questions questions = [None for x in range(NBR)] # Tableau des questions choix_a = [None for x in range(NBR)] # Tableau des propositions A choix_b = [None for x in range(NBR)] # Tableau des propositions B choix_c = [None for x in range(NBR)] # Tableau des propositions C choix_d = [None for x in range(NBR)] # Tableau des propositions D rep_cor = [None for x in range(NBR)] # Tableau des réponses correctes : contient la référence de la bonne réponse rep_uti = [None for x in range(NBR)] # Tableau des réponses utilisateurs : contient la référence de la réponse sélectionnée # Mise en mémoire des questions, propositions et réponses attendues questions[0] = "Quelle est l'année de création de Python ? " choix_a[0] = "1800" choix_b[0] = "1900" choix_c[0] = "1991" choix_d[0] = "1999" rep_cor[0] = choix_c[0] questions[1] = "Que est le mot-clé permettant de déclarer une fonction ? " choix_a[1] = "def" choix_b[1] = "dec" choix_c[1] = "function" choix_d[1] = "new" rep_cor[1] = choix_a[0]

Vous pourrez bien entendu commencer par une seule série de questions avant de gérer plusieurs thèmes.

Le plus facile pour commencer : créer quelques premiers petits tableaux de questions et les utiliser manuellement pour bien comprendre comment faire le lien entre les informations.

Vous pourrez passer ensuite à une réflexion sur l'algorithme.

Propostion 2 de projet : un jeu d'aventure ?

  • On présente à l'utilisateur une situation initiale et on lui propose alors de faire des choix=.
  • En fonction des choix qu'il fait lors de l'aventure, la situation évolue et on lui propose une autre situation dans laquelle il devra encore faire un choix.
  • Les choix doivent aboutir soit à l'échec, soit à la victoire.
  • Une partie de l'aventure devra changer à chaque lancement (l'objet cherché n'est pas nécessairement au même endroit, les monstres sont différents...)
  • On stockera les informations sur le jeu dans différents tableaux :
    • Un tableau pour la description de la sitation actuelle (repérée par son indice i)
    • 3 tableaux pour les propositions d'actions 1 à 3 (repérée par l'indice i)
    • 3 tableaux pour stocker l'indice de la prochaine description à afficher en fonction des actions 1 à 3

    ...EXEMPLE...

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
    # Création de quelques tableaux pour stocker les descriptions des salles NBR = 20 # Nombre de séquences sequences = [None for x in range(NBR)] # Description des séquences actions_1 = [None for x in range(NBR)] # Tableau des actions n°1 actions_2 = [None for x in range(NBR)] # Tableau des actions n°2 actions_3 = [None for x in range(NBR)] # Tableau des actions n°3 destination_si_1 = [None for x in range(NBR)] # Tableau des destinations 1 : à quelle séquence va-t-on avec ce choix destination_si_2 = [None for x in range(NBR)] # Tableau des destinations 2 : à quelle séquence va-t-on avec ce choix destination_si_3 = [None for x in range(NBR)] # Tableau des destinations 3 : à quelle séquencen va-t-on avec ce choix # Mise en mémoire des questions, propositions et réponses attendues sequences[0] = "Vous êtes devant la salle B106. Le cours n'a pas encore commencé. Que faites-vous ?" actions_1[0] = "Vous rentrez en cours, il le faut bien..." actions_2[0] = "Vous décidez d'attendre un peu dans le couloir." actions_3[0] = "Vous descendez en passant par l'escalier." destination_si_1[0] = 1 destination_si_2[0] = 0 destination_si_3[0] = 2 sequences[1] = "Pas de chance. Interro ! Le prof vous demande comment prouver la terminaison d'un algorithme... Que répondre ?" actions_1[1] = "Vous dites qu'il faut attendre et on verra bien." actions_2[1] = "Vous dites que vous n'étiez pas là la fois précedente. Avec un peu de chance, il va accepter cette excuse moisie..." actions_3[1] = "Vous dites qu'il faut réecrire la boucle sous la forme TQ VARIANT > 0 et montrer que le variant est une suite entière décroissante. Trop facile." destination_si_1[1] = 3 destination_si_2[1] = 3 destination_si_3[1] = 4 sequences[2] = "Zone en construction. Que voulez-vous faire ?" actions_1[2] = "Recommencer le jeu." actions_2[2] = "Recommencer le jeu." actions_3[2] = "Arreter." destination_si_1[2] = 0 destination_si_2[2] = 0 destination_si_3[2] = 19

Vous pourrez bien entendu commencer par une aventure statique avant d'intégrer une part d'aléatoire dans les situations proposées.

Le plus facile pour commencer : créer des premiers petits tableaux et les utiliser manuellement pour bien comprendre comment faire le lien entre les informations.

Vous pourrez passer ensuite à une réflexion sur l'algorithme.

Propostion 3 de projet : un générateur aléaoire de scénarios ?

  • On demande un thème à l'utilisateur (Comédie, Sci-Fi, Heroic Fantasy, Zombie, Romantisme...).
  • Le programme propose 5 idées aléatoires de scénarios dans le thème retenu.
  • On propose alors un à un les scénario retenus à trois utilisateurs successifs qui doivent les noter.
  • On affiche alors le scénario ayant la meilleure évaluation.
  • On demande alors aux utilisateurs s'ils veulent recommencer.
  • On stockera les informations sur les thèmes et les scénarios dans différents tableaux (qu'il faudra transmettre aux fonctions via des paramètres) :
    • Un tableau pour la description des gentils possibles pour chaque thème (exemple : "une princesse")
    • Un tableau pour la description des méchants possibles pour chaque thème (exemple : "un dragon")
    • Un tableau pour la description rapide d'une phrase reliant les gentils aux méchants (exemple : "va devoir combattre" )
    • Un tableau pour la description d'un complément d'informations (exemple : "pour parvenir à explorer le monde" )
    • Un tableau pour stocker les 5 idées générées aléatoirement à partir des quatres tableaux précédents.
    • Un tableau pour stocker les évaluations du premier utilisateur.
    • Un tableau pour stocker les évaluations du deuxième utilisateur.
    • Un tableau pour stocker les évaluations du troisième utilisateur.

Le plus facile pour commencer : créer des 4 premiers tableaux pour un thème donné et voir comment on peut les utiliser manuellement pour générer un scénario aléatoire. Les f-strings sont vos amis.

Vous pourrez passer ensuite à une réflexion sur l'algorithme.

5 - FAQ

Tableau ?

1/4 - Création et lecture d'un tableau

Indice 0 1 2 3 4 5 >>> tables = ['Alice', '-', 'Bob', '-', '-', 'Charlie'] >>> tables[0] 'Alice' >>> tables[1] '-' >>> tables[2] 'Bob' >>> tables[3] '-' >>> tables[4] '-' >>> tables[5] 'Charlie' >>> tables[6] IndexError: list index out of range

Pour créer un tableau de 7 cases, dont les indices vont de 0 à 6 :

>>> t = [None for x in range(7)] >>> t [None, None, None, None, None, None, None]

2/4 - Déterminer la longueur d'un tableau

Indice 0 1 2 3 4 5 >>> tables = ['Alice', '-', 'Bob', '-', '-', 'Charlie'] >>> len(tables) 6

3/4 - Association d'une boucle FOR et de LEN

1 2 3
tables = ['Alice', '-', 'Bob', '-', '-', 'Charlie'] for indice in range( len(tables) ): print( tables[indice] )

Si vous lancez ce programme, vous obtiendrez cela :

Alice - Bob - - Charlie

4/4 - Association des données entre plusieurs tableaux

Il suffit de placer les données liées à la même place dans les différents tableaux.

Imaginez ceci :

>>> noms = [None for x in range(5)] >>> superficies = [None for x in range(5)] >>> noms[0] = "la France" >>> superficies[0] = 543940 >>> noms[1] = "la Belgique" >>> superficies[1] = 30528 >>> noms[2] = "l'Allemagne" >>> superficies[2] = 357386 >>> f"La superficie de {noms[0]} est de {superficies[0]} km2" 'La superficie de la France est de 357386 km2' >>> f"La superficie de {noms[2]} est de {superficies[2]} km2" "La superficie de l'Allemagne est de None km2"

Quand l'utilisateur tape une lettre cela provoque une erreur ! On peut faire quelque chose ?

C'est bien le problème des informations qui proviennent d'un humain. Il a tendance à répondre n'importe quoi.

Je ne peux pas vous fournir de solution utilisant uniquement des notions déjà abordées.

Si on résume : on tente toujours de créer des programmes en séparant les fonctions en trois catégories :

  1. Les fonctions d'interface qui agissent avec l'utilisateur (récupération des réponses de l'utilisateur et affichage des réponses du programme)
  2. Les fonctions gérant les données qui ne vont que travailler sur les données : calcul et stockage
  3. Les fonctions de liaison qui gèrent les deux catégories de fonctions précédentes. C'est typiquement dans ces fonctions que se trouvent le coeur du programme : elles réalisent des tâches complexes en faisant appel à d'autres fonctions plus spécialisées.

Activité publiée le 28 08 2019
Dernière modification : 02 01 2022
Auteur : ows. h.