Ont déjà été tentées là, ici, ou encore là, différentes approches pour apporter sur ZesteDeSavoir un cours d’apprentissage du Python pour débutants. Aucune n’a réussi. Comme évoqué dans ce sujet, il s’agit d’un exercice difficile.
C’est pourtant un défi que j’aimerais relever, parce que je trouve l’existant actuellement insuffisant. Ma préférence va vers le cours « Apprendre à programmer avec Python » de Gérard Swinnen, qui a le mérite de s’orienter sur l’apprentissage de la programmation avec Python comme support, plutôt que l’inverse. Mais qui du même coup ne donne pas un bon enseignement des structures du langage (itérations), et qui se porte très vite sur l’aspect graphique (Tkinter).
J’ai alors voulu synthétiser tout ce qui pourrait servir à la rédaction future d’un tel cours. La ligne directrice à suivre, les notions à aborder, les autres apprentissages, et les obstacles qui seront rencontrés au long de la rédaction.
Les notions
Le plus simple à mes yeux est de dresser la liste des notions du langage, elles sont pour moi assez claires. La liste qui suit ne représente pas un ordre particulier dans lequel seraient abordées les notions.
- Il y aurait premièrement les types de données « de base » : nombres entiers, flottants, chaînes de caractères (+ bytes), listes, tuples, dictionnaires et ± ensembles. Avec leurs méthodes et les opérateurs qui s’y appliquent, les priorités de ces opérateurs ;
- Il y aurait aussi les structures du langage : variables, conditions (± ternaires), boucles
while
etfor
, fonctions, imports, exceptions et gestionnaires de contexte (utilisation) ; - Il faudrait décrire la bibliothèque standard (builtins), ses principaux modules (
os
,os.path
/pathlib
,sys
,math
,random
, ±pickle
, ± réseau) ;- Ce qui passerait par de la manipulation de fichiers, de la gestion des paramètres de la ligne de commande (
argparse
).
- Ce qui passerait par de la manipulation de fichiers, de la gestion des paramètres de la ligne de commande (
Mais tout ça sans parler des points essentiels et rébarbatifs, qui ne sont pas des notions à proprement parler, mais les premiers points bloquants que rencontrera un débutant :
- Procédures d’installation de l’interpréteur (avec précisions sur l’OS, sur la version à choisir) ;
- Prise en main de la console, découverte de l’interpréteur interactif ;
- Choix, installation et prise en main d’un éditeur de texte ;
- Écriture du code dans des fichiers (shebang, encodages) ;
- Outils annexes (
pip
, environnement virtuels), installation de bibliothèques complémentaires ; - Partage/distribution d’un programme.
Maintenant, avec l’ensemble de ces points plus une présentation du langage, de ses intérêts et de son histoire, je trouve qu’on commence à avoir un périmètre assez précis.
L'articulation
Reste alors le plus difficile :
- Mettre cela en forme en ayant une ligne suffisamment intéressante pour ne pas perdre le lecteur ;
- Ne pas tomber dans la facilité avec une description du langage, mais bien enseigner la programmation ;
- Rester constamment juste, la moindre erreur pouvant avoir de fortes implications ;
- Cela exclut aussi d’énoncer des choses fausses dans les premiers chapitres pour les corriger par la suite ;
- Être ludique.
Il faut en effet que le lecteur apprenne, que cet apprentissage soit progressif, et donc qu’il s’exerce. Je suis assez favorable à l’idée d’un exercice en tant que fil conducteur, mais la diversité des points à aborder peut parfois se heurter à la réalisation d’une application unique.
Le plus dur pour moi est d’accepter que toutes les informations sur un mot-clef, un type ou une fonction ne pourront pas être centralisées dans un même chapitre : il ne s’agit pas de rédiger une documentation ! Il faut alors penser à un découpage adéquat, un mélange subtil et de bonnes liaisons pour aborder les notions au fur et à mesure.
Élaboration d'un plan
Chapitres de présentation et découverte
Il est évident qu’il faudra débuter par la présentation du langage et l’installation de son interpréteur. Mais je pense qu’il est nécessaire de placer le plus d’informations possibles dans des chapitres annexes (installation de l’interpréteur, d’un éditeur de texte, prise en main de l’éditeur), et d’y faire seulement référence dans le fil principal, afin de ne pas l’alourdir (un utilisateur de Linux n’a que faire de l’installation de Python sous Windows, par exemple).
Ce premier chapitre devra aussi aborder la question des différentes versions de Python. Et montrer un éventail de ce qu’il est possible de réaliser avec ce langage.
Viendra ensuite la prise en main de l’interpréteur, avec des expressions basiques. Classiquement, on retrouve une utilisation de l’interpréteur interactif en mode calculatrice. Ça a le mérite de présenter un type simple (nombres entiers) et quelques opérateurs : il est ensuite facile de commencer à aborder le principe des variables. L’autre avantage est de permettre, par l’opérateur de division, une transition simple des nombres entiers vers les flottants.
Avant d’aller plus loin, il faut encore présenter l’utilisation de fichiers pour écrire son code, et le lancement de l’interpréteur sur un tel fichier.
Il est alors nécessaire d’aborder la fonction print
.
Et impossible de faire l’impasse sur les questions d’encodage, notamment pour les utilisateurs de Windows.
Il serait aussi assez juste d’expliquer quand utiliser l’interpréteur interactif, et quand préférer l’écriture dans un fichier.
D’autres fonctions comme abs
, round
, min
et max
pourraient aussi être présentées dans ces premiers chapitres.
La question du fil conducteur
Arrivé ici, le lecteur a appris à lancer Python, et sait un minimum interagir avec lui. C’est maintenant qu’un fil conducteur devient primordial : il faut que notre lecteur trouve un intérêt à continuer sa lecture. Ce fil déterminera dans quel ordre seront abordées les notions. Comme dit plus tôt, j’aime l’idée d’une application qui serait développée tout le long du cours, et qui serait consolidée par chaque nouvelle notion.
Le cours tel que je l’imagine ne proposerait rien sur la programmation graphique. J’exclus alors toute application qui aurait pour but d’obtenir une interface graphique, et qui serait plutôt décevante sans. Ça appuie par contre l’idée qu’il faudrait un second cours, à la suite de ce premier, sur la prise en main d’une bibliothèque graphique1.
Pour l’aspect ludique, je pense qu’il est préférable de s’orienter vers un jeu. Et pour sa relative simplicité, je pense qu’un combat au tour par tour à la manière de Pokémon peut être une bonne idée.
Premiers types de données
Il est maintenant nécessaire de quitter les nombres et d’aborder les chaînes de caractères.
Viennent alors de nouveaux opérateurs et de nouvelles fonctions (len
).
C’est probablement à partir d’ici que sera utilisé le terme « fonction ».
Puis il est temps d’introduire le concept de variables, leur utilité, leur définition et leur nomenclature. Leur spécificité en Python, aussi, pour les lecteurs qui auraient expérimenté d’autres langages. On noterait aussi les différents opérateurs d’affectation.
Peut ensuite être présentée la fonction input
, pour de premières interactions avec l’utilisateur.
Quelques méthodes simples peuvent aussi être introduites : capitalize
, upper
, lower
, index
.
C’est surtout la différence de syntaxe entre appels de fonctions et de méthodes qui doit être comprise.
À ce moment du cours, il est déjà possible d’avoir une application contenant 4 variables : les noms des deux combattants et leurs points de vie.
Les noms peuvent même être demandés à l’utilisateur via input
.
L’introduction des conversions de type permettrait aussi de demander les PVs.
Le chapitre suivant portera nécessairement sur les conditions.
Premières structures de données
Les conditions vont permettre d’aller plus loin dans les interactions avec l’utilisateur, en lui proposant des choix par exemple.
Seront aussi abordés les opérateurs booléens (and
, or
et not
), et par extension le type bool
, dans un but de vérification des entrées utilisateurs (vérifier qu’un nombre est compris entre deux bornes).
Ce dernier point permettra de présenter les inégalités chaînées en Python (a < b < c
).
Il sera alors temps d’introduire les listes (listes d’attaques), leurs opérateurs (semblables aux str
), et leurs méthodes (index
, append
).
Un premier paragraphe pourra être émis sur la mutabilité (en référence aux chaînes de caractères).
Puis les listes serviraient à aborder les boucles for
. Par exemple, itérer sur les attaques pour présenter les choix à l’utilisateur.
Dans la foulée, on trouverait bien évidemment range
.
J’aurais aussi pensé à enumerate
(pour éviter le recours trop fréquent à range
), voire zip
, mais ça fait peut-être un peu tôt.
Viendrait enfin la boucle while
, pour faire tourner le jeu jusqu’à ce que l’un des deux combattants n’ait plus de PVs.
Je ne sais pas s’il serait judicieux de tout de suite parler des mots-clef break
et continue
, mais il faudra les placer quelque part.
Retour sur les types
Ici, il faudra avant tout évoquer les dictionnaires, pour remplacer les multiples variables représentant un combattant (nom, points de vie, attaques).
Quelques petites digressions montreraient comment utiliser des dictionnaires conjointement aux boucles, on aborderait aussi les méthodes keys
, values
et items
.
Je pense que c’est aussi le bon endroit pour parler des tuples : la plupart des listes utilisées jusque là n’ont aucun intérêt à être modifiables (attaques et listes d’attaques).
À ce point du cours, le lecteur a face à lui un programme fonctionnel de combats au tour par tour.
Les fonctions
Il sera alors temps de factoriser le code, et de montrer comment écrire de nouvelles fonctions. C’est la nécessité de créer des fonctions qui devra d’abord être expliquée. La définition de premières fonctions (sans paramètres ni retours) ne devrait pas poser de problème.
Mais il se posera ensuite celui du scope de la fonction, une fois les paramètres et arguments évoqués. Un lecteur ne devra pas quitter ce chapitre sans avoir compris qu’un paramètre n’existe qu’à l’intérieur d’une fonction, mais que sa valeur provient de l’extérieur (par l’argument). Le modèle de représentation des variables en Python (étiquettes) pourra être rappelé ici, notamment pour évoquer le cas des objets mutables.
Les premières fonctions définies serviront à séparer les affichages d’informations de la logique du code.
Enfin, on s’intéressera aux retours de fonction, afin de pouvoir pleinement les utiliser, et ainsi factoriser les blocs de code en rapport avec les choix utilisateur. Un rappel sur les tuples pourra être présenté pour parler des retours multiples.
Les fichiers
Je pense qu’à ce stade, il serait bon de parler des fichiers, pour enregistrer l’état de la partie par exemple.
Les chapitres concernés seraient progressifs : on commence par un simple fichier texte que l’on open
/close
, on passe ensuite par un bloc with
, on gère les exceptions à l’ouverture/écriture.
Il est alors imaginable de commencer à explorer la bibliothèque standard : struct
, pickle
ou json
pour les fichiers de sauvegarde (les deux premiers ont l’avantage de permettre de parler des fichiers en mode binaire et du type bytes
).
Serait alors introduit le mécanisme d’importation, et ses syntaxes.
J’évoquerais tout de suite la documentation et la fonction help
.
On verrait aussi les modules sys
puis argparse
, afin de recevoir un fichier de sauvegarde au lancement de notre programme.
La séparation de l’application en différents modules aurait aussi sa place ici.
Et ces chapitres pourraient se terminer sur le __name__
et sa fameuse valeur '__main__'
.
Et plus si affinités
Ici, on commencerait par de l’aléatoire pour agrémenter un peu le jeu.
Mais les chapitres de cette partie serviraient aussi à présenter ce qui n’a pas pu l’être jusque là :
- Les listes d’arguments (
*args
,**kwargs
) ; - Les valeurs par défaut des paramètres de fonctions ;
- Les autres méthodes des types de données basiques ;
- Les autres types de données (ensembles,
collections
) ; - La gestion de types (
type
,isinstance
) ; - Les conditions ternaires ;
- Les mots-clef manquant (
else
,finally
,break
,continue
,raise
) ; - L’utilisation des décorateurs ;
- La variable
_
; - Un peu de réseau si besoin ;
- Les modules tiers, leur installation, les environnements virtuels,
pip
.
Cette dernière partie reprendrait une structure un peu plus documentaire : un chapitre consacré à un sujet, et qui fait le tour de la question.
Le plan
Voici donc le plan qui pourrait ressortir des idées listées plus haut. Les titres correspondent à des parties, et les puces de plus haut niveau aux chapitres. Les noms ne sont là qu’à titre indicatif.
Je conviens que la dernière partie est très brouillonne, et devrait être revue de fond en comble et redécoupée. J’ai voulu y détailler ce qu’il était encore nécessaire de voir avant de passer à la suite (aux autres cours), tout en étant facultatif pour le lecteur « pressé ». On y trouve donc plus des chapitres à la clef.
Si je compte bien, on arrive actuellement à 7 parties pour 52 chapitres (de tailles variables), sans compter les annexes. C’est plutôt colossal
Découverte
- Présentation du langage
- Histoire
- Réalisations (ce qu’il est possible d’en faire)
- Versions, interpréteurs
- Installation
- Redirection vers des chapitres annexes
- Installation de l’interpréteur CPython 3.x
- Installation d’un éditeur de texte + configuration
- Prise en main
- Lancement de l’interpréteur interactif
- Expressions basiques (calculatrice), nombres entiers
- Division, nombres flottants
- Écriture du code dans des fichiers
- Lancement de l’interpréteur sur un fichier
- shebang, encodage du fichier
print
- Que choisir entre REPL et fichiers ?
Interagir avec Python
- Retour sur la calculatrice
- Vue d’ensemble des opérateurs (division entière, modulo, puissance)
- Fonctions (introduites en tant que telles) :
abs
,round
,min
,max
- Sauvegarder un résultat : les variables
- Poser une étiquette sur une valeur
- Redéfinition d’une étiquette
- Opérateurs d’affectation
- Variable spéciale
_
- Les chaînes de caractères
- Représenter autre chose que des nombres
- Guillemets simples et doubles
- La fonction
len
- Opérateurs des chaînes de caractères
- Concaténation
- Indexation
- Slicing
- Interagir avec l’utilisateur
- La fonction
input
- Les conversions de type (types en tant que fonctions)
- La fonction
- Les méthodes des objets
- Introduction brève du concept d’objet
- Différence entre fonctions et méthodes
- Les méthodes des chaînes de caractères :
capitalize
,upper
,lower
,index
- Mise en pratique : jeu de combat en tour par tour
- Définir deux monstres : avec noms et points de vie
- Définition au runtime : avec
input
- Attaque unique qui ferait perdre un nombre fixe de PVs
- Un seul tour de jeu
Des programmes moins déterminés
- Les conditions
- Réaliser une action seulement si un test est vérifié
- Tester une égalité (
==
)
- Tester une égalité (
- Blocs de code et indentation
- Clause
else
- Clauses
elif
- Réaliser une action seulement si un test est vérifié
- Les expressions booléennes
- Notion de prédicat
- Opérateurs de comparaison (
==
,!=
,<
,>
,<=
,>=
) - Combiner les prédicats avec les opérateurs booléens (
and
,or
,not
) True
etFalse
, tables de vérité- Toutes les valeurs correspondent à un booléen
- Égalités et inégalités chaînées (
a == b == c
,a < b < c
)
- Ajoutons des conditions à notre jeu
- Proposer à l’utilisateur de choisir les attaques
- Les attaques ne seraient pas définies en tant que tel, mais seraient juste des blocs conditionnels
- Les listes
- Tableaux de valeurs de types divers (entiers, flottants, chaînes, booléens)
- Listes d’attaques (une liste pour les noms, une pour les puissances)
- Opérateurs et premières méthodes (
index
,count
) - Les listes sont modifiables (contrairement aux chaînes)
- Autres méthodes (
append
,insert
,pop
,clear
) - Des listes dans des listes
- Une liste unique pour les attaques
- Itérer sur les valeurs d’une liste
- La boucle
for
for element in liste
- Type
range
: itérer sur des nombres - Fonctions d’itérations :
enumerate
,zip
- La boucle
- Intégration des boucles
for
dans le jeu- Itérer sur les attaques pour les proposer au joueur
- Utiliser l’index retourné par
input
pour localiser la bonne attaque
- Boucler sur une condition
- La boucle
while
- Différence entre
for
etwhile
(entre itérer et boucler) - Usages de la boucle
while
(condition de fin : calculs, attentes)
- La boucle
- Un jeu qui tourne jusque la fin
- Utiliser une boucle
while
pour continuer le jeu tant que l’un des deux monstres a des PVs
- Utiliser une boucle
Retour sur les types
- Rappel des types étudiés jusqu’ici
- Méthodes
str.replace
list.extend
,list.reverse
,list.sort
- Les dictionnaires
- Tables d’association (associer des chaînes à d’autres valeurs)
- Une variable unique pour représenter un monstre
- Opérateurs des dictionnaires (accès aux éléments)
- Les dictionnaires sont modifiables
- Méthodes des dictionnaires :
get
,pop
,update
,clear
- Itérer sur un dictionnaire
- Les dictionnaires peuvent s’utiliser avec
for
- Méthodes
keys
,values
etitems
- Les dictionnaires peuvent s’utiliser avec
- Clefs de dictionnaires
- Les chaînes de caractères ne sont pas le seul type de clefs possible
- Restriction sur les types de clefs (listes)
- Tuples
- Des listes non modifiables
- Remplaçons nos listes par des tuples
- Des tuples comme clefs de dictionnaires
Les fonctions
- Factorisation du code
- Don’t Repeat Yourself
- Comment éviter ces répétitions et réunir les portions logiques ?
- Définition d’une fonction
def toto():
- Le code écrit dans le bloc n’est pas exécuté
- À moins d’appeler la fonction
- Une fonction peut être appelée plusieurs fois, le code sera chaque fois exécuté
- Scope des fonctions
- Les variables définies dans une fonction n’existent qu’à l’intérieur de celle-ci
- Passer des valeurs via les arguments et paramètres
- Les paramètres sont des variables de la fonction, il n’existent que dans son scope
- Les arguments sont les valeurs qui existent à l’extérieur
- Arguments positionnels et arguments nommés
- Paramètres et types mutables
- Attention, la modifiction d’un paramètre altère l’argument
- Redéfinition n’est pas modification
- Intégration des fonctions dans notre application
- Regrouper les commandes d’affichage, paramétrées
- Retours de fonctions
- Renvoyer une valeur de l’intérieur vers l’extérieur
- Le mot-clef
return
- Récupérer la valeur renvoyée
- Renvoyer des valeurs multiples (tuples implicites)
- Affectations multiples
- Swap de variables
Utilisation de fichiers
- Comment couper une partie pour la reprendre plus tard ?
- Lire un fichier en Python
- Fonction
open
- Mode
'r'
- Méthodes
read
etclose
- Bloc
with
- Fonction
- Itérer sur un fichier
- Argument de
file.read
- Méthodes
readline
etreadlines
- Itérer sur un fichier avec
for
- Argument de
- Écrire dans un fichier
- Le mode
'w'
- Méthodes
write
etwritelines
- Fonction
print
- Méthode
seek
- Le mode
'a'
- Le mode
- Les modules
- Notion de module
- Module = fichier
- Importer un module (
import
) - Extraire des données d’un module (
from ... import
) - Se documenter sur un module (
help
)
- Gérer les exceptions
- Le fichier n’existe pas forcément
- Modules
os
etos.path
- Tester la présence d’un fichier avec
os.path.exists
- Mieux : réagir en cas d’erreur
- Blocs
try
/except
- Formater les données
- Module
json
- Ouverture des fichiers en mode binaire
- Le type
bytes
- Module
pickle
- Module
- Arguments de la ligne de commande
- Recevoir le fichier à traiter par la ligne de commande
- Le module
sys
,sys.args
- Utilisation avancée : le module
argparse
- Séparons notre application en modules
- Découpage et factorisation
- Notion de packages
__name__
Aller plus loin
- Un peu d’aléatoire
- Le module
random
- Choisir un nombre :
randint
etrandrange
- Choisir un élément :
choice
,sample
etshuffle
- Simuler une IA dans notre jeu
- Les nombres flottants et les distributions
- Le module
- Retour sur les chaînes de caractères
replace
,split
,join
et autres méthodes
- Les autres types de données
- Ensembles (
set
) - Module
collections
- Ensembles (
- Retour sur les conditions
- Instructions et expressions
- Conditions ternaires
- Retour sur les boucles
- Boucles infinies
- Couper une boucle :
break
- Passer à l’itération suivante :
continue
else
pour savoir comment s’est terminée une boucle
- Retour sur les exceptions
- Réagir quand tout va bien :
else
- Réagir dans tous les cas :
finally
- Lever une exception :
raise
- Réagir quand tout va bien :
- Débogage
- Introspecter une valeur (
help
,dir
,type
) - Présentation de
pdb
- Introspecter une valeur (
- Tour d’horizon de la bibliothèque standard
- Un peu de réseau
- Introduction aux sockets (utilité, familles, types)
- Architecture serveur (
bind
,listen
,accept
) - Architecture client (
accept
) - Envoyer et recevoir des messages
- Programmation multi-threadée
- Instancier des threads
- Programmation concurrente
- Installer des modules/paquets supplémentaires
- Installation par le système d’exploitation
- Installation via
pip
- Environnements virtuels
- Transition vers le chapitre POO + bibliothèque graphique
Voilà pour ce qui est de mes idées sur le sujet. Je n’ai pas prévu de commencer la rédaction de ce cours, mais je garde ça dans un coin de la tête.
Les ressources actuelles évoquées en introduction seraient bien sûr réutilisées dans la mesure du possible, afin de ne pas réécrire l’existant.
Des avis, des remarques, des questions ?