Apprendre À Programmer en Python
Apprendre À Programmer en Python
L’idée de ce cours n’est pas simplement de vous présenter les différents éléments de
Python un-à-un mais véritablement de vous expliquer quand et comment utiliser chaque
nouvelle notion afin que vous ayez le plus rapidement une vue d’ensemble claire pour
utiliser les différents éléments de ce langage au bon moment et à bon escient.
Mon but, à travers ce cours, est de vous amener vers une certaine autonomie et de faire
en sorte que vous soyez capables de réaliser de petits projets Python par vous même à
la fin de celui-ci.
Ce cours est ainsi ouvert à tous : nous commencerons par nous familiariser avec
l’environnement de travail Python avant de présenter des structures simples puis irons
progressivement vers des notions plus complexes.
Chaque nouvelle section dispose d’un script illustré par de nombreux exemples. Dans ce
cours Python, nous travaillerons avec l’interpréteur Python en mode interactif, ce qui
signifie que nous taperons les différentes commandes Python directement dans l’invite
de commande ou le terminal. Je vous invite fortement à faire l’effort de recopier chacun
de mes codes vous même car c’est en pratiquant qu’on apprend le mieux et car c’est
comme cela que vous pourrez vous familiariser le plus vite avec la syntaxe Python et cela
rendra le cours beaucoup plus dynamique pour vous.
Ensuite, nous découvrirons les variables Python, puis les types de données, les structures
de contrôles (conditions et boucles) et les fonctions Python.
Dans un troisième temps, nous nous attaquerons à ce qui fait selon moi le coeur de
Pyhon : l’orienté objet les classes, les instances et les objets.
Nous terminerons finalement avec d’autres opérations et notions qu’il faut connaitre en
Python comme la manipulation des fichiers, l’utilisation des modules , la gestion des
erreurs et un travaille diriger sur l’utilisation du module pygame pour la creation des
jeux videos.
Python est un langage de programmation créé par Guido Van Rossum. La première
version publique du langage est sortie en 1991. Son nom provient de la troupe de
comiques anglais les Monty Python.
Python est un langage interprété, qui se veut simple à comprendre, modulable et aux
applications multiples. C’est aujourd’hui un des langages les plus utilisés et les plus
plébiscités par les développeurs.
Cette première leçon a pour but de présenter le langage Python et de poser quelques
concepts fondamentaux relatifs aux langages de programmation qui nous seront utiles
par la suite.
En tant que développeurs, nous allons vouloir fournir nos séries d’instructions aux
ordinateurs afin qu’ils exécutent certaines tâches précises dans un certain ordre.
Pour cela, il va falloir que l’ordinateur nous comprenne et il va donc falloir qu’on exprime
nos instructions dans un langage qu’il comprend. Le souci, ici, est que les ordinateurs ne
comprennent qu’un seul langage : le binaire ou “langage machine” qui est très opaque
pour nous (les Humains) et très long à écrire.
Pour pallier à ce problème, certaines personnes ont créé ce qu’on appelle des langages
de programmation. Le but premier de tout langage de programmation est de passer des
instructions à l’ordinateur.
Tous les langages de programmation ne sont pourtant pas égaux : certains sont plus
facilement compréhensibles pour nous que d’autres et certains nous permettent de
transmettre uniquement certaines instructions à l’ordinateur.
Python est un langage de programmation dit de “très haut niveau”. Cela signifie qu’il
possède un haut niveau d’abstraction par rapport au langage machine. Pour le dire très
simplement : plus un langage de programmation est de “haut niveau”, plus sa syntaxe se
rapproche de notre langage (l’anglais) plutôt que du langage machine. Un langage de
haut niveau est donc plus facile à comprendre et à utiliser qu’un langage de plus bas
niveau.
En informatique, les langages évoluent par “version”. Les équipes qui créent un langage
travaillent sans cesse à l’amélioration de celui-ci mais chaque changement fait sur le
langage n’est pas immédiatement intégré au langage ni disponible au public.
Au contraire, une liste de modifications / ajouts sont faits au langage en arrière plan et,
dès que l’équipe gérant l’évolution du langage juge les changements suffisamment
importants et stables, une nouvelle “version” du langage qui incorpore ces changement
est distribuée.
La première grosse mise à jour de Python date de 2000 avec la version Python 2. Les
versions se sont ensuite enchainées normalement avec Python 2.1, 2.2, 2.3, etc. jusqu’en
2009 avec la sortie de Python 3.
Pour Python 3, l’équipe qui gère l’évolution du Python a fait le choix audacieux de partir
sur de nouvelles bases et de casser la compatibilité avec les anciennes versions.
Voulant modifier en profondeur leur langage et proposer une version beaucoup plus
“propre” et au goût du jour, la Python Software Fondations (l’équipe chargée du
développement du Python) a donc décidé de faire table rase pour Python 3 et de partir
sur de nouvelles bases.
Le souci ici est que Python 2.x était déjà très populaire à l’époque et que cela allait
mettre de nombreuses personnes dans l’embarras. L’équipe de Python a donc fait le
choix de conserver deux versions du langage : Python 2.x et Python 3.x et de les faire
évoluer de front. Pour être tout à fait précis, la version 3 est la version dont les
fonctionnalités allaient continuer d’évoluer tandis que la version 2 n’allait recevoir que
des mises à jour liées à la sécurité.
Aujourd’hui encore, on retrouve ces deux versions de Python qui co-existent. Pour nous,
cependant, le problème ne se pose pas : nous travaillerons bien évidemment avec la
dernière version en date de Python qui est à l’heure où j’écris la version 3.10.4
Python est un langage de programmation conçu dès le départ pour être “full stack”,
c’est-à-dire pour avoir de multiples applications à la différence du PHP par exemple qui
a clairement été imaginé pour fonctionner dans un contexte Web.
Dans cette leçon, nous mettons en place notre environnement de travail en nous
équipant des logiciels nécessaires pour pouvoir développer en Python.
En pratique, cependant, nous voudrons souvent enregistrer nos différents scripts dans
des fichiers plutôt que d’écrire des séries d’instructions à exécuter immédiatement. Pour
cela, nous avoir besoin d’un éditeur de texte ou d’un IDE (Environnement de Travail
Intégré).
L’interpréteur Python
Python est un langage interprété, ce qui signifie qu’on va avoir besoin d’un programme
nommé interpréteur pour convertir nos instructions Python en un langage
compréhensible par notre ordinateur.
Sur cette page, commencez par télécharger la dernière version stable de Python qui
correspond à votre système. L’interpréteur sera parmi les fichiers téléchargés.
Notez que si vous utilisez un Mac un interpréteur Python devrait déjà être installé sur
votre machine par défaut. Cependant, l’interpréteur pré-installé risque de correspondre à
une ancienne version et nous voulons dans ce cours travailler avec la dernière version
stable du langage (Python 3.10.4 à l’heure de ce tutoriel).
Une fois que vous avez téléchargé la ressource Python disponible sur le site officiel,
ouvrez la en double cliquant dessus. Un processus d’installation démarre. Si vous êtes
sur Windows, cochez la case “Ajouter Python 3.x au PATH”. Suivez ensuite le processus
d’installation comme pour n’importe quel autre logiciel.
Vous venez d’installer l’interpréteur Python ainsi que d’autres ressources pour vous aider
dans vos développements Python, notamment IDLE et Python Launcher si vous utilisez
un Mac.
IDLE est un environnement de développement intégré spécialement pour Python qui
nous permet d’exécuter directement du code Python. Python Launcher permet lui
d’exécuter du code Python créé dans des fichiers séparés .py en double cliquant
simplement sur ces fichiers. Ces deux logiciels sont extrêmement puissants et pratiques,
même si nous ne nous en servirons pas dans ce cours pour faciliter la compréhension et
le suivi de tous.
Pour exécuter nos scripts Python, il va falloir les passer à l’interpréteur. Pour cela, nous
aL’invite de commande se trouve dans le menu Démarrer de Windows sous le nom cmd.
Le terminal se trouve dans le dossier Applications, puis Utilitaire de Mac. Dès que vous
trouvez l’invite de commande ou le terminal, ouvrez le. Une fenêtre noire devrait
apparaitre.
Une fois que vous êtes dans l’invite de commande ou dans le terminal, commencez par
vérifier que l’interpréteur Python a bien été installé. Pour cela, tapez la commande python
-V (avec un V majuscule) et pressez la touche Entrée. Si vous êtes sur Mac, Python 2.7
devrait déjà être installé et sera probablement la version renvoyée. Tapez plutôt la
commande python3 -V. La version de l’interpréteur Python installée devrait vous être
renvoyée.
Ecrire son code directement dans l’invite de commande / le terminal est très pratique
pour tester rapidement un code. Cependant, en conditions “réelles”, c’est-à-dire lorsque
vous devrez créer de vrais programmes contenant potentiellement plusieurs scripts ou
lorsque vous voudrez distribuer votre code, vous stockerez vos instructions dans des
fichiers .py que vous allez créer avec un éditeur de texte ou un IDE.
L’éditeur de texte
Si vous avez décidé de suivre ce tutoriel sur Python, je suppose que vous savez déjà ce
qu’est un éditeur de texte.
Un éditeur de texte nous permet tout simplement d’écrire des lignes de code et
d’enregistrer nos fichiers au bon format.
Il existe des centaines d’éditeur prêt à télécharger sur le Web et la majorité d’entre eux
sont gratuits. Je n’ai pas de conseil spécifique à vous donner quant au choix de votre
éditeur de texte : essayez simplement d’en trouver un avec lequel vous êtes à l’aise et si
possible installez plutôt un IDE (Environnement de Travail Intégré) qu’un simple éditeur.
Pour ma part, dans ce cours, j’utiliserai l’IDE Visual Studio Code de Microsoft mais vous
pouvez utiliser Brackets, Atom, Sublime Text ou encore Komodo si vous préférez.
Notez par ailleurs qu’il existe certains éditeurs spécialement conçus pour écrire du code
Python dont le célèbre PyCharm notamment .
Syntaxe de base et exécution
d’instructions Python
Dans cette leçon, nous allons découvrir quelques éléments de syntaxe de base de
Python. Nous allons également exécuter nos première instructions Python en utilisant
l’interpréteur de manière interactive ou en créant un fichier Python qu’on va ensuite
passer à l’interpréteur.
L’intérêt principal d’un fichier est qu’il peut être sauvegardé et distribué. En conditions
réelles, vous utiliserez donc souvent votre éditeur de texte et créerez des fichiers Python
pour mener à bien des projets.
Dans ce cours, j’écrirai directement dans mon terminal pour vous montrer les résultats
d’instructions simples et nous ne créerons des fichiers que lorsque cela sera nécessaire.
Cependant, pour cette première leçon concrète, je voulais tout de même vous montrer
comment créer un fichier Python et comment exécuter ce fichier.
Pour créer un fichier Python, nous allons déjà ouvrir notre éditeur de texte, allons ouvrir
un nouveau fichier et enregistrer ce fichier en lui donnant une extension .py. On peut
appeler ce fichier cours.py par exemple et l’enregistrer dans un dossier Python sur notre
bureau.
Ici, on commence par enregistrer notre fichier au format Python avant même d’écrire un
quelconque code car cela va permettre à notre éditeur de reconnaitre le type de code
qu’on souhaite écrire et d nous proposer des fonctionnalités relatives à cela. Par ailleurs,
il est possible que votre éditeur vous propose d’installer des extensions spécifiques
Python pour faciliter l’écriture, l’affichage et le debugging de votre code. Si c’est le cas,
n’hésitez pas à les installer.
Ensuite, ajoutez les lignes de code suivantes dans votre fichier (nous allons expliquer par
la suite ce qu’elles signifient) :
Ici, si vous avez bien ajouté Python au PATH, vous n’avez qu’à taper cours.py pour
exécuter le code du fichier “cours.py”. Si votre système possède plusieurs versions de
Python, comme c’est le cas sur Mac, il faudra préciser la version utilisée : on tapera
alors python3 cours.py.
Si Python n’a pas été ajouté au PATH, il va falloir préciser où le fichier se trouve. Pour
cela, on va commencer par taper la commande cd Desktop. La commande cd signifie
“change directory” (“changer de dossier”).
Ensuite, on n’a plus qu’à préciser l’emplacement de notre fichier par rapport à notre
bureau (Desktop), c’est à dire dans notre cas Python/cours.py (Mac) ou Python\
cours.py (Windows) pour exécuter notre fichier, en précisant éventuellement également
une version si plusieurs versions sont installées sur notre système ( python3
Python/cours.py pour Mac).
Cette fonctionnalité est très utile lorsqu’on souhaite tester rapidement des bouts de
code ou pour l’apprentissage.
Pour utiliser l’interpréteur Python de cette manière, on va déjà devoir l’appeler depuis
l’invite de commande ou le terminal. Pour cela, on écrit la commande Python3.7 et on
presse Entrée pour valider.
Ensuite, on a plus qu’à écrire nos différentes instructions et à presser Entrée afin que
l’interpréteur les interprète immédiatement.
Les commentaires servent à “commenter” le code : ils vont être ignorés lors de
l’exécution du code (ils n’auront aucun impact sur le script) et vont donc être totalement
transparents pour l’utilisateur final.
On utilise généralement les commentaires pour documenter un code (pour soi ou pour
d’autres développeurs si le code a vocation à être distribué) ou pour empêcher son
exécution. En Python, on utilise le signe # pour écrire un commentaire dans notre code.
Python n’a pas de syntaxe particulière pour écrire des commentaires multi-lignes. Pour
écrire un commentaire sur plusieurs lignes, on insérera un # au début de chaque ligne.
L’indentation Python
L’indentation correspond au fait de créer des retraits au niveau de certaines lignes de
code. Dans la grande majorité des langages, l’indentation n’a qu’une visée esthétique :
elle permet à un script d’être plus lisible pour un développeur.
Généralement, une bonne indentation est censée représenter les dépendances au sein
d’un code : on ajoutera un retrait à chaque fois qu’une instruction est incluse / dépend
d’une autre.
En Python, l’indentation a un tout autre rôle : elle est utilisée par Python pour définir des
blocs de code, c’est-à-dire pour indiquer à l’interpréteur quelle instruction appartient à
quelle autre.
Si on indente mal notre code Python, celui-ci ne s’exécutera tout simplement pas et
Python renverra une erreur. Pas de panique, nous allons apprendre au cours de ce cours
à quelle moment indenter. Cela est cependant relativement évident et ne devrait pas
vous poser de problème : dès qu’il y a une relation de dépendance, il faudra ajouter un
retrait (une fabulation).
Dans le code ci-dessus, par exemple, nous avons utilisé une structure conditionnelle if.
Le principe de cette structure est simple : on lui fournit un test et SI le test est validé,
alors on exécute un code. Le code à exécuter dépend du résultat du test; on effectuera
donc un retrait pour mentionner ce code afin que Python puisse bien s’apercevoir qu’il
dépend de la structure conditionnelle.
Si vous avez l’habitude d’autres langages de programmation comme le PHP par exemple
cela peut être un peu déconcertant au départ. Vous pouvez alors considérer que
l’indentation remplace l’usage des accolades.
La fonction print ()
Les fonctions sont d’autres éléments de langage que nous étudierons plus tard. Grosso-
modo, une fonction est définie par un nom et une série d’instructions. Lorsqu’on appelle
une fonction (en utilisant son nom et un couple de parenthèses juste derrière), elle
exécute toutes les instructions qu’elle contient. Les fonctions servent généralement à
effectuer une action précise qui peut être plus ou moins complexe.
En effet, cette fonction va être très précieuse puisqu’elle va également nous permettre
d’afficher des résultats auquel on n’a pas directement accès. Nous illustrerons cela dans
la suite de ce cours; faites moi confiance pour le moment.
Pour créer une variable en Python, on va donc devoir choisir un nom et affecter une
valeur à ce nom, c’est-à-dire stocker une valeur dans notre variable.
Le choix du nom pour nos variables est libre en Python. Il faut cependant respecter les
règles usuelles suivantes :
Pour affecter ou “assigner” une valeur à une variable, nous allons utiliser un opérateur
qu’on appelle opérateur d’affectation ou d’assignation et qui est représenté par le
signe =. Attention, le signe = ne signifie pas en informatique l’égalité d’un point de vue
mathématique : c’est un opérateur d’affectation.
Le signe = ne sert pas à dire que la valeur est égale au nom de variable ou que la
variable “vaut” cette valeur, il indique simplement qu’on affecte ou qu’on stocke une
certaine valeur dans un conteneur.
D’un point de vue mathématique, par exemple, écrire x = x + 2 n’a aucun sens car la
variable x ne peut être égale qu’à elle même. En informatique, cependant, c’est tout à
fait possible puisque l’opérateur = n’est pas un opérateur d’égalité mais d’affectation.
Ecrire x = x + 2 signifie qu’on affecte une nouvelle valeur à x qui est égale à l’ancienne
valeur de x à laquelle on ajoute 2.
Notez déjà qu’on va également pouvoir effectuer des affectations multiples en Python,
c’est-à-dire affecter plusieurs valeurs à plusieurs variables très facilement en utilisant la
syntaxe nom_variable_1, nom_variable_2, nom_variable_3 = valeur1, valeur2, valeur3 .
Il est important de connaitre le type de valeur stocké dans une variable car nous n’allons
pas pouvoir effectuer les même opérations avec une variable qui stocke un nombre ou
avec une variable qui stocke une chaine de caractères par exemple.
Nous étudierons les spécificités liées à chaque type de valeur que peut stocker une
variable Python dans la prochaine leçon.
Afficher et modifier le contenu d’une
variable Python
Nous allons pouvoir réaliser toutes sortes d’opérations avec nos variables. La plus
basique d’entre elles consiste à afficher le contenu d’une variable. Pour afficher la valeur
d’une variable Python, il suffit d’écrire son nom, ou éventuellement d’utiliser la
fonction print() en renseignant le nom de la variable entre les parenthèses/
Comme je l’ai précisé plus haut, l’une des caractéristiques fondamentales des variables
va être de pouvoir stocker différentes valeurs durant sa vie. Ainsi, on peut tout à fait
affecter une nouvelle valeur à une variable. Une variable « classique » ne va pouvoir
stocker qu’une valeur à la fois. En affectant une nouvelle valeur à une variable, la valeur
précédente est ainsi écrasée.
Pour comprendre ce code, vous devez savoir qu’ici les instructions sont exécutées
linéairement, c’est-à-dire dans leur ordre d’écriture. On commence par affecter la valeur
29 à notre variable age et la valeur Pierre à notre variable prenom, puis on affiche le
contenu de ces variables.
On affecte ensuite de nouvelles valeurs dans nos variables. Ces nouvelles valeurs
remplacent (“écrasent”) les précédentes. Lorsqu’on affiche à nouveau le contenu de nos
variables, les valeurs renvoyées sont les dernières stockées.
Imaginons par exemple qu’on crée un programme qui va effectuer des calculs à partir
d’une valeur qui va lui être envoyée uniquement lorsque ce programme sera exécuté.
Lors de la création du programme, on ne connait pas cette valeur et cette valeur va
pouvoir être différente entre différentes exécutions du programme.
On va pouvoir indiquer que cette valeur (inconnue pour le moment) devra être stockée
dans telle variable au début du programme et ensuite définir les différentes opérations
qui devront être effectuées sur cette valeur en utilisant le nom de la variable (qui sera
remplacée par sa valeur effective lorsque le programme s’exécutera).
Un “type” de données pour un langage est défini par les manipulations qu’on va pouvoir
faire sur ces données : on va pouvoir effectuer les mêmes opérations sur chaque donnée
d’un même type et des opérations différentes sur différents types de données.
Prenons un nombre entier par exemple. Il semble tout à fait logique de pouvoir effectuer
des opérations mathématiques de base avec ce nombre : addition, soustraction,
multiplication, etc. En revanche, on ne va pas pouvoir effectuer les mêmes opérations /
manipulations sur un texte, puisque multiplier un texte par quelque chose par exemple
n’a aucun sens.
Les types de données Python
Python définit de nombreux types de données qu’on va pouvoir stocker dans nos
variables et manipuler à loisir ensuite : nombres entiers, décimaux, complexes, chaines
de caractères, booléens, listes, tuples, dictionnaires, etc.
Dans cette leçon, nous allons nous contenter de passer en revue les types de données
les plus simples à se représenter (nombres, chaines et booléens). Les autres types de
données feront l’objet de leçons séparées.
Pour effectuer des opérations arithmétiques entre différentes valeurs numérique, nous
allons devoir utiliser des opérateurs arithmétique (+ pour l’addition, - pour la
soustraction, etc.).
On peut également stocker des nombres dans des variables et utiliser ces variables pour
effectuer nos calculs. Ici, on commence par définir une variable x et on lui attribue la
valeur 5. On effectue ensuite différentes opérations. Notez que durant tous ces
calculs x continue de stocker 5 puisqu’on n’affecte jamais de nouvelles valeurs à x.
Finalement, on définit une deuxième variable y = 10 et on demande à Python de
calculer x + y, c’est-à-dire de nous donner le résultat de l’addition des deux valeurs
contenues dans x et dans y.
Le type de données “nombre décimal” ou float couvre tous les nombres décimaux (c’est-
à-dire les nombres à virgule) ainsi que certaines expressions scientifiques comme le e qui
désigne une exponentielle.
Nous allons grosso-modo pouvoir réaliser les mêmes opérations avec des données de
type float qu’avec des données de type int (même si le traitement en arrière plan va être
différent).
Si notre chaine de caractères contient elle même des guillemets simples (apostrophes)
ou doubles, il faudra les échapper les uns ou les autres en fonction du délimiteur choisi
pour qu’ils soient correctement interprétés car sinon Python pensera qu’ils servent à
indiquer la fin de la chaine. Le caractère d’échappement en Python est l’antislash \.
Dans l’exemple ci-dessus, on veut afficher et stocker la chaine Je m’appelle “Pierre
Giraud” qui contient à la fois des guillemets simples ou apostrophes et des guillemets
doubles. Il va donc falloir qu’on échappe les uns ou les autres en fonction du caractère
de délimitation de chaine choisi afin d’indiquer au Python que les guillemets simples ou
doubles à l’intérieur de notre chaine ne représentent pas la fin de la chaine, c’est-à-dire
pour lui indiquer qu’ils ne possèdent pas ce sens spécial de délimiteurs.
Notez qu’on peut également utiliser une syntaxe alternative utilisant des triples
guillemets simples ou doubles pour entourer notre chaine et qui nous dispense ainsi
d’avoir à échapper les apostrophes et les guillemets dans notre chaine :
Lorsqu’on utilise l’interpréteur en mode interactif, comme on le fait dans ce cours,
l’interpréteur affiche les chaines entre guillemets simples. On peut utiliser la
fonction print() pour afficher les chaînes de manière plus lisible, en retirant notamment
les guillemets simples et doubles :
J’ai dit plus haut que pour définir une chaine de caractères il fallait l’entourer de
guillemets simples ou doubles droits. Réciproquement, toute valeur entourée par des
guillemets simples ou doubles sera considéré par Python comme une valeur de
type str (“str” = “string” = chaine de caractères).
Ainsi, si on stocke un ou plusieurs caractères représentant des chiffres comme “1”, “2”,
“123”, etc. en les entourant de guillemets simples ou doubles ceux-ci seront considérés
comme des valeurs de type str et non comme des valeurs de type int, code>float
ou complexe et on ne pourra pas effectuer les mêmes opérations.
Comme vous pouvez le voir, on n’obtient pas du tout les mêmes résultats lorsqu’on
additionne deux nombres ou lorsqu’on additionne (ou plus exactement concatène) deux
chaines.
Finalement, vous pouvez également remarquer que Python nous renvoie une erreur
lorsqu’on tente d’additionner / de concaténer un entier avec une chaine.
Pour stocker un booléen dans une variable, il ne faut pas utiliser de guillemets : si on les
utilise, ce seront les chaines de caractères “True” et “False” qui seront stockés et on ne va
pas pouvoir effectuer les mêmes opérations.
Les valeurs booléennes sont très utilisées en informatique. Elles sont notamment très
utiles pour valider ou invalider un test et sont au coeur des différentes structures de
contrôle en général.
Pour connaitre le type de valeur stockée dans une variable, on peut utiliser la fonction
Python type(). On va passer la variable à tester en argument de cette fonction (c’est-à-
dire écrire le nom de la variable entre les parenthèses de la fonction). La
fonction type() va alors tester la valeur contenue dans la variable et renvoyer le type de
cette valeur.
Ne faites pas attention pour le moment à l’indication “class” également renvoyée par la
fonction type(), nous verrons ce que ça signifie bien plus tard dans ce cours.
Nous étudierons les autres types d’opérateurs plus tard dans ce cours, lorsque nous en
aurons besoin pour illustrer certaines notions de Python.
Opérateur Nom
+ Addition
– Soustraction
* Multiplication
Opérateur Nom
/ Division
% Modulo
** Puissance
// Division entière
Le modulo correspond au reste d’une division Euclidienne (division entière) tandis que
l’opérateur // permet d’obtenir le résultat entier d’une division (ou la partie entière de ce
résultat pour être tout à fait exact).
Le résultat de 13//3, par exemple, sera 4 tandis que le résultat de 13%3 sera 1 car dans le
cas d’une division entière 13/3 = 4 avec reste = 1.
Ces deux symboles remplissent donc un rôle relativement similaire que lorsqu’ils sont
utilisés avec des données de type numériques mais attention tout de même de ne pas
confondre le + utilisé pour additionner deux nombres et le + utilisé pour concaténer
deux chaines de caractères : on distingue ici bien deux opérateurs différents même si le
même symbole est utilisé.
Equivalent
Opérateur Exemple Description
à
Vous pouvez également déjà noter qu’il existe également les opérateurs d’affectation
combinés &=, |=, ^=, >>= et <<= qui vont permettre d’effectuer des opérations dont nous
discuterons plus tard et d’affecter le résultat de ces opérations directement à une
variable.
Dans cette leçon, nous allons découvrir un premier type de données composites Python :
les listes. Nous allons comprendre l’intérêt de ce type de données et apprendre à les
manipuler.
Notre liste est ici composée de 5 valeurs de type numérique. On va pouvoir stocker tous
types de valeurs dans une liste, comme des chaines de caractères par exemple :
De plus, vous devez savoir que tous les éléments d’une liste n’ont pas à être du même
type, on va très bien pouvoir créer des listes composées de nombres, chaines et
booléens par exemple :
Note : Si vous avez déjà étudié un langage de script par le passé, les liste doivent vous
faire penser à ce qu’on appelle communément dans ces autres langages des tableaux. En
effet, les listes Python sont très proches des tableaux (numérotés) qu’on peut retrouver
dans de nombreux autres langages.
Les listes possèdent des indices numériques qui commencent à 0. La première valeur
d’une liste possède donc toujours l’indice 0, la deuxième valeur l’indice 1, la troisième
valeur l’indice 2 et etc.
Pour récupérer une valeur en particulier dans une liste, on va devoir préciser le nom de
la liste suivi de l’indice de cette valeur entre crochets. Notez que les indices négatifs sont
acceptés; dans ce cas on partira de la fin de la liste (l’indice -1 correspond au dernier
élément, -2 à l’avant dernier et etc.).
On va également pouvoir récupérer une tranche de valeurs dans une liste, c’est-à-dire un
ensemble de valeurs qui se suivent. Pour cela, on utilisera le symbole : entre les crochets
avec 0, 1 ou 2 indices autour.
Comme pour les listes, le premier caractère d’une chaîne possède l’indice 0, le deuxième
l’indice 1 et etc; On va également pouvoir utiliser des indices négatifs et récupérer des
tranches avec :.
Notez qu’il n’existe pas de type distinct pour les caractères en Python : un caractère est
simplement une chaîne de longueur 1.
En effet, vous devez bien comprendre qu’une fois qu’on définit une valeur “chaine de
caractères” par exemple, celle-ci ne peut plus être modifiée par la suite. Les seules
opération qu’on va pouvoir faire vont être de créer une nouvelle chaine en concaténant
deux chaines d’origine (qui une nouvelle fois ne seront pas modifiées) ou de remplacer
une chaine par une autre valeur en affectant une nouvelle valeur dans une variable (ce
qui a pour effet d’écraser la chaine de départ).
Au contraire des chaines, on va tout à fait pouvoir ajouter, modifier ou supprimer des
valeurs dans une liste. Pour cela, on va mentionner le nom de notre liste avec l’indice de
la ou des éléments à ajouter / modifier et leur affecter une nouvelle valeur. Les
affectations de tranches sont possibles :
Dans cette leçon, nous allons nous intéresser à un nouveau type de données composites
Python qui peut faire penser aux listes à premières vue : les tuples. Nous allons souligner
les différences entre les listes et les tuples, voir les cas d’usage des tuples et apprendre à
créer des tuples.
Les tuples peuvent contenir différents types de valeurs comme des nombres, des
chaines, des listes etc. et même d’autres tuples imbriqués. Illustrons immédiatement cela
:
La grande différence entre un tuple et une liste est qu’un tuple est une donnée
immuable à la différence d’une liste qui est altérable. Cela signifie qu’on ne va pas
pouvoir modifier les valeurs d’un tuple après sa création.
Il va donc être intéressant d’utiliser des tuples plutôt que des listes dans les cas où on
veut s’assurer que les données ne soient pas modifiées dans un programme.
Le déballage de séquence
Une fonctionnalité intéressante des tuples est le “déballage de séquence”. Un déballage
de séquence correspond à une façon rapide d’affecter les différentes valeurs d’un tuple
dans des variables séparées. Pour cela, il va nous falloir autant de variables qu’il y a de
valeurs dans notre tuple. Ensuite, il va falloir respecter la syntaxe variable1, variable2 … =
tuple comme ceci :
Attention ici : il faut bien faire attention à écrire les variables qui vont recevoir les valeurs
du tuple avant le tuple car dans le cas contraire cela ne fonctionnerait pas.
Les dictionnaires Python
Dans cette leçon, nous allons nous intéresser à un autre type de données Python
composites : les dictionnaires. Nous allons découvrir l’intérêt des dictionnaires et
apprendre à manipuler ce nouveau type de données.
La grande différence entre les données séquentielles et les dictionnaires se situe dans la
façon d’indexer les valeurs et dans la nature de l’index. Dans le cas des séquences, les
différentes valeurs dont associées à des index numériques commençant à 0.
Les dictionnaires nous laissent une bien plus grande liberté ici puisqu’on va pouvoir
choisir nous mêmes nos clefs (ou index ou indice) et attribuer la clef de notre choix à
chaque valeur à partir du moment où cette clef n’est pas déjà utilisée dans ce
dictionnaire et où la clef est une valeur immuable. La conséquence de cela est que les
valeurs d’un dictionnaire ne sont pas ordonnées à la différence des valeurs d’une
séquence.
Si vous avez des notions en PHP ou dans certains autres langages de script, vous pouvez
retenir que les dictionnaires Python sont l’équivalent des tableaux associatifs dans ces
langages.
Pour cela, on va tout simplement utiliser le nom de notre dictionnaire suivi d’un couple
de crochets [ ] en passant une clef entre ces crochets puis affecter une valeur à cette clef.
Dans le cas où la clef existe déjà, l’ensemble clef : valeur n’est pas ajouté mais la valeur
originellement liée à la clef est plutôt remplacée par la nouvelle valeur.
Pour créer un ensemble, nous allons utiliser une paire d’accolades { } en placer les
différents éléments de notre ensemble entre ces accolades en les séparant avec une
virgule.
Les types de données simples étudiés sont les Nombre (entier, décimaux ou complexes),
les Booléens et les Chaines de caractères. Il sont facile à manier et il est simple de savoir
quand utiliser une type plutôt qu’un autre.
Les types de données composite étudiés sont les listes, les tuples, les dictionnaires et les
ensembles. Il est généralement moins évident de choisir quel type de données utiliser ici
car on a tendance à penser “qu’ils se ressemblent tous”.
Voici donc un résumé des grandes caractéristiques de ces types et se qui les différencie :
Les listes sont des collections d’éléments ordonnés et altérables qui peuvent
contenir plusieurs fois la même valeur ;
Les tuples sont des collections d’éléments ordonnés et immuables qui peuvent
contenir plusieurs fois la même valeur ;
Les dictionnaires sont des collection d’éléments non ordonnés mais indexés avec
des clefs de notre choix et altérables qui n’acceptent pas de contenir plusieurs
fois le même élément ;
Les ensembles sont des collections d’éléments non ordonnées, non indexés et
non modifiables qui n’acceptent pas de contenir plusieurs fois le même élément
La condition if (“si”) ;
La condition if…else (“si…sinon”) ;
La condition if…elif…else (“si…sinon si… sinon”) .
Nous allons étudier et comprendre l’intérêt de chacune de ces conditions dans la suite
de cette leçon. Avant de les étudier, cependant, nous allons devoir présenter un nouveau
type d’opérateurs : les opérateurs de comparaison qui vont être au centre de nos
conditions.
Pour pouvoir faire cela, nous allons comparer la valeur d’une variable à une certaine
autre valeur donnée et selon le résultat de la comparaison exécuter un bloc de code ou
pas. Pour comparer des valeurs, nous allons devoir utiliser des opérateurs de
comparaison.
Opérateur Définition
< Permet de tester si une valeur est strictement inférieure à une autre
> Permet de tester si une valeur est strictement supérieure à une autre
<= Permet de tester si une valeur est inférieure ou égale à une autre
>= Permet de tester si une valeur est supérieure ou égale à une autre
Notez bien ici que ces opérateurs ne servent pas à indiquer à Python que telle valeur est
supérieure, égale, inférieur ou différente à telle autre valeur. Lorsqu’on utilise un
opérateur de comparaison, on demande au contraire à Python de tester si telle valeur est
supérieure, égale, inférieur ou différente à telle autre valeur. Python va donc comparer
les deux valeurs et toujours renvoyer un booléen : True si la comparaison est vérifiée
ou False dans le cas contraire.
La condition if en Python
La structure conditionnelle if est une structure de base qu’on retourne dans de
nombreux langages de script. Cette condition va nous permettre d’exécuter un code si
(et seulement si) une certaine condition est vérifiée.
On va en fait passer une expression à cette condition qui va être évaluée par Python.
Cette expression sera souvent une comparaison explicite (une comparaison utilisant les
opérateurs de comparaison) mais pas nécessairement.
Dans notre deuxième if, on demande cette fois-ci à Python de nous dire si le contenu
de x est égal au chiffre 5. Ce n’est pas le cas et donc Python renvoie False et le code dans
ce if n’est donc pas exécuté.
Au final, vous pouvez retenir que toute expression qui suit un if va être évaluée par
Python et que Python renverra toujours soit True, soit False. Nous n’avons donc pas
nécessairement besoin d’une comparaison explicite pour faire fonctionner un if.
Pour comprendre cela vous devez savoir qu’en dehors des comparaisons Python
évaluera à True toute valeur passée après if à l’exception des valeurs suivantes qui seront
évaluées à False :
La valeur 0 (et 0.0) ;
La valeur None ;
Les valeurs chaine de caractères vide ””, liste vide [], dictionnaire vide {} et tuile
vide ().
Notre deuxième condition fait exactement le même travail mais cette fois si on compare
la valeur de yà 5.
Notez bien ici qu’on n’effectuera jamais de test dans un else car le else est par définition
censé prendre en charge tous les cas non pris en charge par le if.
Il faut cependant faire attention à un point en particulier lorsqu’on utilise une structure
Python if… elif… else : le cas où plusieurs elif possèdent un test évalué à True par Python.
Dans ce cas là, vous devez savoir que seul le code du premier elif (ou du if si celui-ci est
évalué à True) va être exécuté. En effet, Python sort de la structure conditionnelle dans
son ensemble sans même lire ni tester la fin de celle-ci dès qu’un cas de réussite à été
rencontré et que son code a été exécuté.
Les opérateurs logiques vont nous permettre de créer des conditions plus puissantes
mais dans certains cas il sera plus intéressant et plus rapide d’imbriquer des conditions.
Notre deuxième condition teste si y contient une valeur strictement inférieure à 10. Si
c’est le cas, on exécute le code dans la condition. Sinon, on va directement au else de
cette condition imbriquée.
Dans le cas où on utilise and, chaque expression devra être évaluée à True par Python
pour que le code dans la condition soit exécuté.
Dans le cas où on utilise or, il suffit qu’une expression soit évaluée à True par Python
pour que le code dans la condition soit exécuté.
Ici, vous pouvez noter que Python a l’inverse de la plupart des autres langages possède
une syntaxe très logique et très intuitive qui va nous permettre d’effectuer plusieurs tests
dans une condition “comme si” on utilisait un opérateur logique and en utilisant tout
simplement plusieurs opérateurs de comparaison à la suite.
Je vous recommande cependant plutôt d’utiliser des opérateurs logiques dans cette
situation afin de rendre votre code plus clair.
Ces opérateurs ne vont fonctionner qu’avec des séquences (chaines de caractères, listes,
etc.) et ne vont donc pas marcher avec des valeurs de type numérique par exemple.
Comme des valeurs booléennes sont renvoyées, on va tout à fait pouvoir utiliser ce type
d’opérateurs au sein de nos conditions même si ils sont communément plus utilisés au
sein de boucles qu’on étudiera dans la prochaine leçon.
A partir de là, cependant, il faut commencer à être très rigoureux pour obtenir des
résultats conformes à nos attentes et il va notamment falloir bien connaitre l’ordre de
priorité d’application des différents opérateurs en Python.
Ci-dessous, vous pourrez trouver l’ordre de traitement de chaque opérateur Python
(classés du plus prioritaire au moins prioritaire). Certains nous sont encore inconnus, je
vous demande de ne pas vous en préoccuper pour le moment.
Opérateur Description
() Opérateur de groupement
** Elévation à la puissance
Lorsqu’on code, on va en effet souvent devoir exécuter plusieurs fois un même code.
Utiliser une boucle nous permet de n’écrire le code qu’on doit exécuter plusieurs fois
qu’une seule fois.
Nous allons ainsi pouvoir utiliser les boucles pour parcourir les valeurs d’une variable de
liste liste ou pour afficher une suite de nombres.
Lors du premier tour dans la boucle, x stocke la valeur 0 qui est bien inférieure à 10. On
rentre donc dans la boucle, on affiche la valeur de x et on ajoute 1 à cette valeur.
Une fois arrivés en fin de boucle, on retourne au début de la boucle. On teste à nouveau
si x contient une valeur inférieure à 10. C’est le cas puisque x stocke désormais 1. On
affiche à nouveau la valeur de x et on lui ajoute à nouveau 1.
Note : Lorsqu’on ajoute 1 à une variable, on dit qu’on l’incrémente. À l’inverse, lorsqu’on
enlève 1 à la valeur d’une variable, on dit qu’on la décrémente. Les opérations
d’incrémentation et de décrémentation sont très fréquentes au sein des boucles. On s’en
sert généralement pour que la condition d’exécution de la boucle soit fausse à un
moment donnée.
En effet, la boucle for Python va nous permettre d’itérer sur les éléments d’une séquence
(liste, chaine de caractères, etc.) selon leur ordre dans la séquence.
La condition de sortie dans cette boucle va être implicite : on sortira de la boucle après
avoir parcouru le dernier élément de la séquence.
La fonction range()
On va pouvoir utiliser la fonction range() pour itérer sur une suite de nombres avec une
boucle for.
Cette fonction permet de générer une suite de valeurs à partir d’une certain nombre et
jusqu’à un autre avec un certain pas ou intervalle.
Dans son utilisation la plus simple, nous allons nous contenter de passer un nombre en
argument (entre les parenthèses) de range(). Dans ce cas, la fonction génèrera une suite
de valeurs de 0 jusqu’à ce nombre – 1 avec un pas de 1. range(5) par exemple génère les
valeurs 0, 1, 2, 3 et 4.
Par exemple, on va pouvoir stopper l’exécution d’une boucle lorsqu’une variable contient
une valeur en particulier.
L’intérêt principal des fonctions se situe dans le fait qu’on va pouvoir appeler une
fonction et donc exécuter les instructions qu’elle contient autant de fois qu’on le
souhaite, ce qui constitue au final un gain de temps conséquent pour le développement
d’un programme et ce qui nous permet de créer un code beaucoup plus clair.
Il existe deux grands “types” de fonctions en Python : les fonctions prédéfinies et les
fonctions créées par l’utilisateur.
En fait, ces deux fonctions sont des fonctions complexes et qui contiennent de
nombreuses lignes d’instructions leur permettant d’accomplir une tâche précise :
l’affichage d’un résultat ou la détermination du type d’une valeur en l’occurence.
Cette complexité nous est cachée : nous n’avons qu’à appeler nos fonctions pour
qu’elles fassent leur travail et n’avons pas à écrire la série d’instructions qu’elles
contiennent à chaque fois et c’est tout l’intérêt des fonctions.
Python, dans sa version 3.7.4, met à notre disposition quasiment 70 fonctions prédéfinies
(sans compter les fonctions des modules ou extensions dont nous parlerons plus tard).
Vous pouvez déjà trouver la liste ci-dessous. Nous serons amenés à utiliser la plupart
d’entre elles dans ce cours ; nous les définirons à ce moment là. Considérez le tableau ci-
dessous comme une simple référence.
Les fonction Python définies par l’utilisateur
En plus des fonction prédéfinies, Python nous laisse la possibilité de définir nos propres
fonctions. Ces fonctions ne seront bien évidemment disponibles et utilisables que dans
l’espace où elles ont été définies, c’est-à-dire uniquement au sein de nos scripts et non
pas pour l’ensemble des développeurs utilisant Python.
On va vouloir créer nos propres fonctions Python lorsque nos programmes utilisent de
manière répétées une même série d’instructions : plutôt que de réécrire ces instructions
à chaque fois, autant utiliser une fonction !
Les fonctions vont aussi être de très bons outils d’abstraction lorsqu’on voudra distribuer
notre code : on préférera souvent fournir des fonctions à utiliser aux autres
développeurs plutôt que de les laisser se débrouiller avec des séries d’instructions
“sauvages”.
Pour définir une nouvelle fonction en Python, nous allons utiliser le mot clef def qui sert
à introduire une définition de fonction. Ce mot clef doit être suivi du nom de la fonction,
d’une paire de parenthèses au sein desquelles on pourra fournir une liste de paramètres
(nous reviendrons là dessus plus tard) et de : pour terminer la ligne comme ceci def
ma_fonction():.
Le nom d’une fonction Python doit respecter les normes usuelles concernant les noms :
un nom de fonction doit commencer par une lettre ou un underscore et ne contenir que
des caractères alphanumériques classiques (pas d’accent ni de cédille ni aucun caractère
spécial).
Notez que les noms de fonctions sont sensibles à la casse en Python, ce qui signifie que
les fonctions ma_fonction(), Ma_fonction(), ma_FONCtion() et MA_FONCTION() par exemple
seront des fonctions bien différentes pour Python.
Nous allons ensuite placer la liste des différentes instructions de notre fonction à la ligne
suivant sa définition et en les indentant par rapport à la définition afin que Python
comprenne que ces instructions appartiennent à notre fonction. Notez que la première
instruction d’une fonction peut être une chaîne de caractères littérale qui sera alors
utilisée comme chaine de documentation de la fonction.
Créons immédiatement deux fonctions bonjour() et BONJOUR() toutes simples dont le
but va être d’afficher un message en exécutant elles-mêmes une fonction print(). On va
faire cela comme cela :
Nous avons ici défini nos deux premières fonctions. Ces fonctions ne sont pas très utiles
ici : elles se contentent simplement d’exécuter une fonction print() mais c’est déjà un bon
début !
Maintenant que nos fonctions sont créées, nous allons devoir les appeler pour exécuter
le code qu’elles contiennent. Pour cela, nous allons utiliser leur nom suivi d’un couple de
parenthèses. On va pouvoir appeler nos fonctions autant de fois qu’on le souhaite dans
notre script : c’est tout l’intérêt des fonctions !
Elles ne sont pas très utiles en l’état. Un autre aspect fondamental des fonctions est
qu’elles vont pouvoir accepter des informations qui viennent de l’extérieur, c’est-à-dire
qui sont externes à leur définition et qui vont les amener à produire des résultats
différents. Souvent même, les fonctions vont avoir besoin qu’on leur passe des
informations externes pour fonctionner normalement.
C’est par exemple le cas des fonctions print() et type() : ces deux fonctions permettent
d’afficher un message et de déterminer le type d’une donnée. Pour afficher un
message, print() va avoir besoin qu’on lui passe les données qu’elle doit afficher. De
même, type() va avoir besoin qu’on lui fournisse la donnée dont elle doit déterminer le
type.
Ces informations dont vont avoir besoin certaines fonctions pour fonctionner et qu’on va
passer à nos fonctions entre le couple de parenthèses sont appelées des arguments ou
des paramètres.
Pour rester très simple et très schématique ici, on parle de “paramètres” lorsqu’on définit
une fonction, c’est-à-dire lorsqu’on indique dans la définition de la fonction que telle
fonction a besoin d’une, de deux… informations pour fonctionner et on parle
“d’arguments” pour désigner les valeurs effectivement passées à une fonction lorsqu’on
l’utilise.
On peut donner n’importe quel nom à ce paramètre dans la définition puisque celui-ci
sera dans tous les cas remplacé par la valeur effective passée lors de l’appel à la fonction.
Pour une meilleure lisibilité, il est cependant conseillé de fournir des noms descriptifs et
paramètres des fonctions. On peut par exemple l’appeler prenom dans notre cas :
Dans la définition de la fonction, on va donc indiquer que notre fonction a besoin d’un
paramètre pour fonctionner. Ici, on choisit le mot “prenom” pour définir notre
paramètre. On aurait aussi bien pu choisir “toto”. Ensuite, dans le corps de notre
fonction, on utilise une fonction print() qui va afficher “Bonjour” suivi du prénom qu’on
aura indiqué lorsqu’on utilisera la fonction.
Ici, je vous rappelle que le mot qu’on utilise comme paramètre lors de la définition de la
fonction sera remplacé par la valeur passée en argument lors de l’appel à la fonction. On
va donc utiliser notre paramètre dans print() afin que cette fonction affiche bien
“Bonjour” suivi d’un prénom.
On utilise ensuite notre fonction plusieurs fois, en lui passant un prénom différent à
chaque fois en argument. La valeur passée va se substituer au paramètre défini lors de la
définition de la fonction.
Le code de notre fonction n’est cependant pas très optimisé ici : en effet, on utilise de la
concaténation dans print() or la concaténation ne va fonctionner que si la valeur passée
est bien une chaine de caractères. Si on passe un un chiffre en argument de bonjour(),
Python renverra une erreur.
Ici, il va être plus efficace de passer le texte et l’argument comme deux arguments
différents de print(). En effet, vous devez savoir que print() est capable d’accepter un
nombre infini d’arguments qu’elle affichera à la suite. Cela résout notre problème de
type de valeurs :
Notions avancées sur les paramètres
des fonctions Python
Dans cette leçon, nous allons étudier quelques concepts relativement avancés liés au
nombre de paramètres des fonctions en passant en revue différentes syntaxes qui vont
nous permettre de passer un nombre variable d’arguments à une fonction.
Cette définition impose qu’on passe un et un seul argument à notre fonction pour
qu’elle fonctionne : si on tente de l’appeler sans argument ou en lui passant plusieurs
arguments Python renverra une erreur.
Dans certaines situations, nous voudrons créer des fonctions plus flexibles qui pourront
accepter un nombre variable d’arguments. Cela peut être utile si on souhaite créer une
fonction de calcul de somme par exemple qui devra additionner les différents arguments
passés sans limite sur le nombre d’arguments et sans qu’on sache à priori combien de
valeurs vont être additionnées.
En Python, il existe deux façons différentes de créer des fonctions qui acceptent un
nombre variable d’arguments. On peut :
Définir des valeurs de paramètres par défaut lors de la définition d’une fonction ;
Utiliser une syntaxe particulière permettant de passer un nombre arbitraire
d’arguments.
Utiliser des valeurs par défaut pour les paramètres de fonctions permet donc aux
utilisateurs d’appeler cette fonction en passant en omettant de passer les arguments
relatifs aux paramètres possédant des valeurs par défaut.
On va pouvoir définir des fonctions avec des paramètres sans valeur et des paramètres
avec des valeurs par défaut. Attention cependant : vous devez bien comprendre qu’ici, si
on omet de passer des valeurs lors de l’appel à la fonction, Python n’a aucun moyen de
savoir quel argument est manquant. Si 1, 2, etc. arguments sont passés, ils
correspondront de facto au premier, aux premier et deuxième, etc. paramètres de la
définition de fonction.
Pour cette raison, on placera toujours les paramètres sans valeur par défaut au début et
ceux avec valeurs par défaut à la fin afin que le arguments passés remplacent en priorité
les paramètres sans valeur.
Si on souhaite s’assurer que les valeurs passées à une fonction vont bien correspondre à
tel ou tel paramètre, on peut passer à nos fonctions des arguments nommés. Un
argument nommé est un argument qui contient le nom d’un paramètre présent dans la
définition de la fonction suivi de la valeur qu’on souhaite passer comme ceci : argument =
valeur.
On va pouvoir passer les arguments nommés dans n’importe quel ordre puisque Python
pourra faire le lien grâce au nom avec les arguments attendus par notre fonction. Notez
cependant qu’il faudra ici passer les arguments nommés en dernier, après les arguments
sans nom. Par ailleurs, aucun argument ne peut recevoir de valeur plus d’une fois. Faites
donc bien attention à ne pas passer une valeur à un argument sans le nommer puis à
repasser cette valeur en le nommant par inattention.
Passer un nombre arbitraire d’arguments avec *args et
**kwargs
La syntaxe *args (remplacez “args” par ce que vous voulez) permet d’indiquer lors de la
définition d’une fonction que notre fonction peut accepter un nombre variable
d’arguments. Ces arguments sont intégrés dans un tuple. On va pouvoir préciser 0, 1 ou
plusieurs paramètres classiques dans la définition de la fonction avant la partie variable.
Ici, on utilise une boucle for pour itérer parmi les arguments : tant que des valeurs sont
trouvées, elles sont ajoutées à la valeur de s. Dès qu’on arrive à court d’arguments,
on print() le résultat.
Dans cette exemple, j’utilise la méthode Python items() dont le rôle est de récupérer les
différentes paires clefs : valeurs d’un dictionnaire. Nous reparlerons des méthodes
lorsque nous aborderons l’orienté objet. Pour le moment, vous pouvez considérer
qu’une méthode est l’équivalent d’une fonction.
On, utilisera la syntaxe *args pour séparer les arguments présents dans une liste ou un
tuple et la syntaxe **kwargs pour séparer les arguments présents dans un dictionnaire et
fournir des arguments nommés à une fonction.
Or, en programmation, nous voudrons souvent récupérer le résultat d’une fonction afin
de l’utiliser dans le reste de notre script. Pour cela, il va falloir qu’on demande à notre
fonction de retourner (renvoyer) le résultat de ses opérations. Nous allons pouvoir faire
cela en Python grâce à l’instruction return.
Pour cela, on va préciser les différentes valeurs que doit retourner return en les séparant
par des virgules. Les valeurs retournées seront retournées dans un tuple.
Vous devez savoir qu’une fonction peut également s’appeler elle même dans son
exécution : c’est ce qu’on appelle la récursivité. Lorsqu’on définit une fonction récursive,
il faudra toujours faire bien attention à fournir une condition qui sera fausse à un
moment ou l’autre au risque que la fonction s’appelle à l’infini.
L’exemple de fonction récursive par excellence est la définition d’une fonction qui
calculerait une factorielle. La factorielle d’un nombre est le produit des nombres entiers
inférieurs ou égaux à celui-ci; la factorielle de 4 par exemple est égale à 4 * 3 * 2 * 1.
Le terme de “portée des variables” sert à désigner les différents espaces dans le script
dans lesquels une variable est accessible c’est-à-dire utilisable. En Python, une variable
peut avoir une portée locale ou une portée globale.
Variables globales et variables locales en
Python
Les variables définies dans une fonction sont appelées variables locales. Elles ne peuvent
être utilisées que localement c’est-à-dire qu’à l’intérieur de la fonction qui les a définies.
Tenter d’appeler une variable locale depuis l’extérieur de la fonction qui l’a définie
provoquera une erreur.
Cela est dû au fait que chaque fois qu’une fonction est appelée, Python réserve pour elle
(dans la mémoire de l’ordinateur) un nouvel espace de noms (c’est-à-dire une sorte de
dossier virtuel). Les contenus des variables locales sont stockés dans cet espace de noms
qui est inaccessible depuis l’extérieur de la fonction.
Cet espace de noms est automatiquement détruit dès que la fonction a terminé son
travail, ce qui fait que les valeurs des variables sont réinitialisées à chaque nouvel appel
de fonction.
Les variables définies dans l’espace global du script, c’est-à-dire en dehors de toute
fonction sont appelées des variables globales. Ces variables sont accessibles (=
utilisables) à travers l’ensemble du script et accessible en lecture seulement à l’intérieur
des fonctions utilisées dans ce script.
Pour le dire très simplement : une fonction va pouvoir utiliser la valeur d’une variable
définie globalement mais ne va pas pouvoir modifier sa valeur c’est-à-dire la redéfinir. En
effet, toute variable définie dans une fonction est par définition locale ce qui fait que si
on essaie de redéfinir une variable globale à l’intérieur d’une fonction on ne fera que
créer une autre variable de même nom que la variable globale qu’on souhaite redéfinir
mais qui sera locale et bien distincte de cette dernière.
Cela est possible en Python. Pour faire cela, il suffit d’utiliser le mot clef global devant le
nom d’une variable globale utilisée localement afin d’indiquer à Python qu’on souhaite
bien modifier le contenu de la variable globale et non pas créer une variable locale de
même nom.
Vous trouverez dans la suite de cette leçon quelques définitions de fonctions qui
pourront vous servir par la suite.
En lui passant trois arguments, le premier argument servira de départ pour la séquence,
le deuxième indiquera la fin de la séquence (avec le nombre indiqué exclu) et le
troisième indiquera le “pas”, c’est-à-dire l’écart entre chaque nombre renvoyé.
Le nombre de décimales par défaut est 0, ce qui signifie que la fonction retournera
l’entier le plus proche.
On peut lui passer autant d’arguments qu’on souhaite comparer de valeurs. Notez qu’on
peut également comparer des chaines même si max() est peu souvent utilisée pour faire
cela.
La fonction min(), au contraire, retourne la plus petite valeur d’une donnée itérable. Elle
s’utilise exactement comme max().
La fonction dir(), lorsqu’elle est utilisée sans argument, renvoie la liste des variables et
des fonctions (ou plus exactement des objets et des méthodes) disponibles dans
l’espace de portée courant. Nous étudierons ce que sont les objets et méthodes par la
suite.
Si on lui passe une donnée en argument, elle renverra la liste des méthodes disponibles
pour ce type de donnée.
Introduction à l’orienté objet en Python
La programmation orientée objet (POO) est un passage obligé lors de l’apprentissage de
nombreux langage informatiques et est également un sujet relativement ardu lorsqu’on
nous le présente pour la première fois.
Cette première leçon a pour objectif de définir le plus simplement et le plus clairement
les concepts d’objets, de classes et d’attributs et de démystifier le sujet de l’orienté objet.
Il est possible que vous ne compreniez pas tout d’un coup : pas d’inquiétude, suivez le
cours à votre rythme, avec moi, et les choses deviendront de plus en plus claires au fil de
votre avancement de le cours. N’hésitez pas à relire cette partie en entier une fois que
vous l’avez terminée pour revoir tous ces concepts sous un oeil nouveau.
La programmation orientée objet repose sur le concept d’objets qui sont des entités qui
vont pouvoir posséder un ensemble de variables et de fonctions qui leur sont propres.
En fait, quasiment tout en Python est avant tout un objet et nous avons manipulé des
objets depuis le début de ce cours sans nous en rendre compte : les types str, int, list, etc.
sont avant tout des objets, les fonctions sont des objets, etc.
Pour véritablement maitriser Python et utiliser toutes ses fonctionnalités, il est donc
indispensable de comprendre cette composante orienté objet.
Vous pouvez déjà noter que Python parle “d’attributs” pour désigner les variables et
fonctions d’un objet et plus précisément “d’attributs de données” pour désigner les
variables d’un objet et de “méthodes” pour désigner les fonctions qui lui sont propres.
Dans la plupart des langages informatiques, on parle plutôt de “membres” pour désigner
les variables et fonctions d’un objet et de “propriété” pour désigner les variables et de
“méthodes” pour désigner les fonctions.
Une classe est également un ensemble cohérent de code qui contient généralement à la
fois des variables et des fonctions et qui va nous servir de plan pour créer des objets
possédant un même ensemble de d’attributs de données et de méthodes de base.
En fait, on peut aller jusqu’à considérer que les classes sont les principaux outils de la
POO puisqu’elles permettent de mettre en place des concepts fondamentaux de la POO
comme l’héritage, l’encapsulation ou le polymorphisme qui sont des concepts qu’on
expliquera et qu’on étudiera en détail plus tard.
Pour le moment, contentez vous de retenir qu’une classe va servir de plan de création
pour un type d’objets. Créer une nouvelle classe en Python correspond à définir un
nouveau type d’objets ou un nouveau type de données.
Pour créer des objets à partir d’une classe en Python, on va utiliser cette classe comme
une fonction. Pour illustrer concrètement comment cela fonctionne et pour que vous
compreniez plus facilement, créons immédiatement une première classe qu’on va
appeler Utilisateur.
Je vous accorde que ce code peut faire peut à priori car il contient de nombreuses
nouvelles choses. Pas d’inquiétude, nous allons l’expliquer au fur et à mesure.
Ici, on crée une nouvelle classe Utilisateur avec la syntaxe class Utilisateur:. Notez que par
convention le nom d’une classe commence toujours par une majuscule.
Lorsqu’on crée un objet à partir d’une classe comme ceci, on dit également qu’on
instancie une classe (on crée une nouvelle instance d’une classe). Ici, on instancie deux
fois notre classe et on place le résultat dans deux variables pierre et mathilde qui
deviennent automatiquement des objets de type Utilisateur.
La chose que vous devez absolument comprendre ici est que les objets créés à partir
d’une classe en Python vont automatiquement avoir accès aux variables et fonctions
définies dans la classe qui vont être pour eux des attributs de données et des méthodes.
Pour accéder aux attributs de données et aux méthodes d’un objet, il va falloir
mentionner l’objet suivi de l’opérateur . (point) suivi du nom de l’attribut de donnée ou
de la méthode à laquelle on souhaite accéder comme ceci :
Ce n’est pas tout : nos objets vont également pouvoir définir des valeurs personnalisées
pour leurs attributs de données et on va également pouvoir définir de nouveaux
attributs de données qui vont être propres à un objet en particulier dans une classe :
Conclusion sur cette introduction à l’orienté
objet Python
Nous venons de faire une première (grosse) introduction à la programmation orientée
objet et avons dans cette leçon défini et découvert de nombreuses nouvelles entités
comme les objets, les classes, etc. et de nouveaux concepts comme l’encapsulation,
l’héritage, etc.
Si vous ne comprenez pas tout pour le moment et si vous êtes un peu perdu, aucune
inquiétude : c’est tout à fait normal. Considérez qu’entamer la POO est un petit peu
comme si vous commenciez ce cours.
Plus vous allez avancer dans cette partie, plus les différents éléments vont faire sens et
mieux vous comprendrez chaque chose car la plupart des concepts présentés ici ont
besoin que vous connaissiez d’autres concepts pour être compris mais pour vous
présenter ces autres concepts j’ai besoin de vous présenter avant les premier concepts
bref… C’est l’histoire du serpent qui se mord la queue ou de l’oeuf et la poule : il faut
choisir un angle d’attaque pour expliquer la suite et progresser.
Pour le moment, je vous conseille de ne pas trop vous formaliser sur cette nouvelle
syntaxe ni sur les nouveaux termes. Essayez simplement de retenir ces deux points pour
la suite :
Les classes sont des ensembles de code qui contiennent des variables et des
fonctions et qui vont nous servir à créer des objets ;
Les objets créés à partir d’une classe disposent automatiquement des variables et
des fonctions définies dans la classe.
Créer une nouvelle classe en Python revient à créer un nouveau type d’objet et de fait un
nouveau type de données. On va ensuite pouvoir instance notre classe pour créer des
objets qui vont partager les variables et fonctions de leur classe.
Pour désigner les variables et les fonctions que les objets héritent de leur classe, Python
utilise les termes “attributs de données” et “méthodes”.
Les termes employés sont différents (et le sont dans tous les langages qui supportent
l’orienté objet) car ils servent à désigner des éléments de langage différents.
L’idée principale à retenir ici est qu’un attribut de donnée ou une méthode est propre à
un objet tandis qu’une variable ou une fonction est indépendante de tout objet. C’est la
raison pour laquelle pour accéder à un attribut de données ou à une méthode on doit
préciser le nom de l’objet qui souhaite y accéder avant.
Si on tente d’accéder à un attribut de donnée ou à une méthode définis dans une classe
sans objet ou à partir d’un objet d’une autre classe, Python renverra une erreur
puisqu’encore une fois les attributs de données de classe et les méthodes de classes
sont propres et ne sont partagés que par les objets de la classe. C’est le principe
“d’encapsulation” que nous allons expliquer en détail juste après.
En plus de cela, notez qu’un objet peut également définir ses propres attributs de
données ou surcharger des attributs de données de classe.
En effet, dans tous les langages de programmation, il est considéré comme une bonne
pratique de limiter le recours aux variables globales car cela rend le code non flexible,
non modulable, et dur à entretenir.
Pour comprendre cela, il faut penser au fait que la plupart des programmes aujourd’hui
contiennent des dizaines de fichiers qui contiennent chacun des centaines de lignes de
code et qui font appel à de nombreux modules externes, c’est-à-dire à des code
préconçus fournis par d’autres personnes.
Dans ces conditions, on ne peut pas se permettre de déclarer ses variables ou fonction
n’importe comment car les risques de conflits, c’est-à-dire les risques qu’un même nom
de variable ou de fonction soit utilisé plusieurs fois pour définir plusieurs variables ou
fonctions entre fichiers et modules sont grands.
Pour cette raison, un bon développeur fera tout pour compartimenter son code en
créant des espaces de portée ou “espaces de noms” bien définis et dont les éléments ne
pourront pas entrer en conflit avec les autres.
Les classes nous permettent de mettre en place cela puisque chaque objet créé à partir
d’une classe sa posséder SES attributs de données et SES méthodes qui ne vont pas être
accessibles depuis l’extérieur de l’objet et qui ne vont donc pas polluer l’espace global.
Pour réaliser cela, nous allons modifier notre classe et lui ajouter une fonction spéciale
appelée __init__() (deux underscores avant et deux après le nom) qui permet
“d’initialiser” ou de “construire” nos objets.
L’idée générale est la suivante : notre fonction __init__() va être construite de telle sorte à
ce que les arguments passés soient utilisés comme valeur d’initalisation pour les
attributs d’une instance. On va passer les argument lors de l’instanciation,
via Utilisateur() dans notre cas et ces arguments vont être transmis à __init__().
Observons de plus près notre fonction __init__(). Comme vous pouvez le voir, celle-ci
accepte trois paramètres en entrée qu’on a ici nommé self, nom et age.
Il est maintenant temps de vous expliquer ce que signifie ce self qui était déjà présent
dans notre dernière définition de classe. Pour cela, il faut retourner à notre définition des
méthodes.
Si vous vous rappelez bien, je vous ai dit au début de cette partie que quasiment tout en
Python était avant tout un objet et qu’en particulier les fonctions étaient des objets de
“type” fonction. Les fonctions d’une classe ne font pas exception : ce sont également
avant tout des objets.
Ce que vous devez absolument comprendre ici est qu’une des particularités des
méthodes est que l’objet qui l’appelle est passé comme premier argument de la fonction
telle que définie dans la classe. Ainsi, lorsqu’on écrit pierre.getNom() par exemple,
l’objet pierre est passé de manière implicite à getNom().
C’est la raison pour laquelle nos fonctions de classe possèdent toujours un paramètre de
plus que d’arguments qui leur sont fournies lorsqu’elles sont appelées en tant que
méthode : l’objet qui les appelle prendra la place de ce paramètre. Par convention, on
appelle ce paramètre self qui signifie “soi-même” pour bien comprendre que c’est l’objet
lui même qui va être passé en argument.
Notez ici qu’en Python “self” ne signifie rien et qu’on pourrait tout aussi bien utiliser un
autre nom, à la différence de nombreux autres langages orienté objet où self est un mot
clef réservé.
Cette fonction va également recevoir l’objet qui est en train d’être créé en premier
argument. Cet objet va donc remplacer le “self”. Ici, notre fonction __init__() sert à
effectuer deux affectations : self.user_name = nom et self.user_age = age.
Lorsqu’on écrit pierre = Utilisateur("Pierre", 29), les deux arguments passés “Pierre” et “29”
vont être transmis avec l’objet à __init__() qui va les utiliser pour créer deux attributs de
données user_name et user_age spécifique à l’objet pierre créé.
Retenez également qu’on évitera de créer des variables de classe avec des données
altérables comme des listes ou des dictionnaires sauf dans des cas très précis. En effet,
une variable de classe “appartient” à tous les objets de la classe par défaut.
Si la variable contient des données altérables, alors n’importe quel objet va pouvoir
modifier ces données. Or, si un objet modifie les données d’une telle variable de classe,
le contenu de la variable sera modifié pour tous les objets de la classe.
Cela est dû au fait qu’une variable de classe est en fait “partagée” par tous les objets de
la classe. On dit que chaque objet de la classe accède à la variable par référence.
Héritage et polymorphisme en Python
orienté objet
L’héritage et le polymorphisme forment avec le principe d’encapsulation les trois piliers
de la programmation orientée objet. Dans cette leçon, nous allons présenter et étudier
ces concepts en détail et comprendre pourquoi et comment les implémenter dans notre
code.
Par défaut, la sous-classe hérite de toutes les variables et fonctions de la classe parent
(et notamment de sa fonction __init__()) et peut également définir ses propres variables
et fonctions. On va ensuite pouvoir instancier la classe enfant pour créer de nouveaux
objets et ces objets vont avoir accès aux variables et fonctions définies à la fois dans la
sous-classe et dans la classe de base.
Ce principe d’héritage va nous permettre de créer des classes de base qui vont définir
des fonctionnalités communes à plusieurs classes puis des sous-classes qui vont hériter
de ces fonctionnalités et pouvoir également définir leurs propres fonctionnalités.
Cela permet in-fine d’obtenir un code plus modulable, mieux organisé, plus clair et plus
concis qui sont des objectifs majeurs pour tout bon développeur.
On ne parlera de surcharge que pour les méthodes car dans le cas des variables définir
une variable avec le même nom dans la sous-classe correspond finalement à créer une
variable locale à la sous-classe en plus de celle “globale” (celle disponible dans la classe
de base) mais ces deux variables continuent d’exister à part entière et à être différente
tandis que lorsqu’on réécrit une méthode la nouvelle méthode remplace véritablement
l’ancienne pour les objets de la sous-classe.
On va pouvoir faire cela en appelant la méthode de notre classe mère depuis notre
classe fille avec la syntaxe ClasseDeBase.nomDeMethode(), ce qui va nous permettre de
récupérer l’intégralité du code de la classe mère.
La réponse à cette question est très simple : Python va rechercher les variables et
méthodes dans un ordre précis.
Python va en fait “remonter” le long de la chaine d’héritage des classes jusqu’à trouver
l’information demandée. Si. Elle n’est jamais trouvée, il renverra finalement une erreur.
Notez ici que Python nous fournit également deux fonctions pour nous permettre de
tester le type d’une instance et l’héritage d’une classe.
Ces deux fonctions sont très utiles pour tester rapidement les liens hiérarchiques entre
certaines classes et objets et peuvent aider à comprendre plus facilement un script
complexe qui nous aurait été passé.
Ici, j’utilise un nouveau mot clef pass qui me sert à créer une fonction vide. En effet, le
mot clef pass ne fait strictement rien en Python. On est obligés de l’utiliser pour créer une
fonction vide car si on n’écrit rien dans notre fonction l’interpréteur Python va renvoyer
une erreur.
Ma classe Animaux dispose donc d’une fonction seDeplacer() qui ne contient pas
d’instruction. Maintenant, nous allons créer des sous-classes de Animaux pour différents
animaux : Chien, Aigle et Dauphin par exemple.
Ces trois sous classes vont par défaut hériter des membres de leur classe
mère Animaux et notamment de la méthode seDeplacer(). Ici, chacune de nos sous classes
va implémenter cette méthode différemment, c’est-à-dire va la définir différemment.
Le polymorphisme permet également in-fine d’obtenir un code plus clair, plus lisible et
plus cohérent : on va pouvoir fournir des définitions de fonctions vides dans une classe
de base afin de laisser des sous-classes implémenter (définir) ces fonctions de
différentes manières.
Héritage multiple
Pour terminer cette leçon sur l’héritage et le polymorphisme, il faut savoir que Python
gère également une forme d’héritage multiple.
Dans la pratique, l’héritage multiple est une chose très difficile à mettre en place au
niveau du langage puisqu’il faut prendre en charge les cas où plusieurs classes mères
définissent les mêmes variables et fonctions et définir une procédure pour indiquer de
quelle définition la sous-classe héritera.
En Python, dans la majorité des cas, l’héritage va se faire selon l’ordre des classes mères
indiquées et cela de manière récursive. Imaginons qu’une sous-classe AGrave hérite de
trois classes A, Accent et Abracadabra dans cet ordre et que la classe A hérite elle même
de la classe Alphabet tandis que Abracadabra hérite de Mot
Les membres privés auxquels on ne peut accéder que depuis l’intérieur de lac
classe ;
Les membres protégés auxquels on ne peut accéder que depuis l’intérieur de la
classe ou depuis une classe fille ;
Les membres publics auxquels on peut accéder depuis n’importe quelle instance
(ou objet) de la classe ou d’une classe fille.
Ces niveaux de visibilité permettent de “protéger” certains membres de classes qui ne
devraient pas être modifiés dans n’importe quelle situation ou depuis n’importe quel
endroit.
En revanche, certaines conventions ont été mises en place par la communauté Python,
notamment au niveau des noms des membres de classe qui servent à indiquer aux
autres développeurs qu’on ne devrait accéder à tel membre que depuis l’intérieur de la
classe ou à tel autre membre que depuis la classe ou une classe fille.
Attention ici : ce ne sont que des conventions qui n’ont aucun équivalent réel en
Python : on va pouvoir informer d’autres développeurs du niveau de visibilité souhaité
pour un membre mais tous les membres seront toujours publics en Python et il est de la
responsabilité des autres développeurs de suivre nos indications ou pas.
On préfixera les noms des membres qu’on souhaite définir comme “privés” avec
deux underscores comme ceci : __nom-du-membre ;
On préfixera les noms des membres qu’on souhaite définir comme “protégés”
avec un underscore comme ceci : _nom-du-membre.
Il est à noter que ces conventions n’ont pas été adoptées par hasard. En effet, Python
possède un mécanisme appelé “name mangling” qui fait que tout membre de classe
commençant par deux underscores, c’est-à-dire de la forme __nom-du-membre sera
remplacé textuellement lors de l’interprétation par _nom-de-classe__nom-du-membre.
Cela fait que si un développeur essaie d’utiliser un membre défini avec deux underscores
tel quel, Python renverra une erreur puisqu’il préfixera le nom avec un underscore et le
nom de la classe du membre.
Cette règle a été prévue par Python pour éviter les accidents de conflits entre plusieurs
membres de plusieurs classes qui auraient le même nom. Elle n’empêche pas d’accéder
ou de modifier un membre “privé”. En effet, il suffit de préfixer le membre de la même
façon que Python lors de son interprétation pour y accéder.
Maintenant, créons un objet à partir de cette classe et essayons d’afficher les valeurs de
nos variables de classe à partir de l’objet créé :
Comme vous le voyez, on accède sans souci à nos variables public et _protected. En
revanche, pour __private, le mécanisme de name mangling s’active et le nom passé est
préfixé par un underscore et le nom de la classe. On peut cependant contourner cela et
toujours afficher le contenu de la variable privée en utilisant la
notation _Visibiilite__private.
Il est possible que vous ne voyez pas l’intérêt de tout cela si vous n’avez pas beaucoup
d’expérience en programmation. Dans ce cas, il est important de retenir et de vous
persuader qu’une immense partie du travail d’un développeur est de s’assurer d’avoir un
code le plus clair, concis et modulable possible (en plus d’être fonctionnel bien
évidemment). Toutes ces notions liées à l’orienté objet permettent de servir ces objectifs.
Et si vous vous posez toujours la question : oui, on pourrait arriver à des résultats
similaires en utilisant une approche non orientée objet mais la qualité générale de notre
code serait bien plus mauvaise. De plus, Python nous fournit de nombreux outils et
notamment des méthodes pour les types prédéfinis qui ne sont accessible qu’en utilisant
l’orienté objet et il serait dommage de s’en priver.
Les itérateurs
“Itérer” signifie en français classique “répéter, faire une seconde fois”. Dans le contexte
de la programmation orienté objet, “itérer” sur un objet signifie parcourir l’objet attribut
par attribut pour accéder à leur valeur.
Les itérateurs sont implicitement utilisés chaque fois que nous manipulons des
collections de données comme les list, tuple ou string (qui sont des objets dits
“itérables”).
La méthode habituelle pour parcourir une collection est d’utiliser une boucle for.
Lorsque nous utilisons une boucle for pour parcourir un objet itérable, la boucle appelle
en fait en interne la fonction iter() sur l’objet itérable.
Au lieu d’utiliser la boucle for comme indiqué ci-dessus, nous pouvons utiliser la
fonction itérateur iter(). Un itérateur est un objet qui représente un flux de données. Il
retourne un élément à la fois.
Chaque fois que __next__() est appelée, l’élément suivant du flux itérateur est renvoyé.
Lorsqu’il n’y a plus d’éléments disponibles, __next__() lève une erreur StopIteration.
Pour cela, il suffit de définir une méthode __iter__() qui renvoie un objet disposant d’une
méthode __next__(). Si la classe définit elle-même la méthode __next__(),
alors __iter__() peut simplement renvoyer self.
Ici, on crée une classe qui définit un itérateur qui va sauter une donnée de la séquence
sur laquelle il Isère à chaque nouveau passage.
Les générateurs
La création d’itérateurs peut parfois s’avérer complexe ou contraignante. Les générateurs
sont des outils qui nous permettent de créer des itérateurs.
La syntaxe d’un générateur est la même que celle d’une fonction classique à la différence
que les générateurs utilisent l’instruction yield à la place de return pour retourner des
données.
Ces fonctionnalités font des générateurs des outils privilégiés pour créer des itérateurs
de manière plus simple.
Annexe 2 : Quelques méthodes Python
utiles
En programmation orientée objet, tout élément d’un langage (ou presque) est objet. En
Python, les types de données sont en fait avant tout représenté par des classes qui
déterminent les propriétés et manipulations qu’on va pouvoir faire sur chaque donnée.
Lorsqu’on crée une liste, un dictionnaire, une chaine de caractères, etc. on crée avant
tout un nouvel objet à partir des classes list, dict, str, etc.
La plupart de ces classes définissent des méthodes qui permettent de réaliser des
opérations courantes sur chaque type de donnée. Dans cette leçon, nous allons passer
en revue quelques unes des méthodes qui pourront certainement vous être utiles par la
suite et qu’il faut connaitre pour les types de données str, list, et dict.
Pour obtenir la liste complète d’un méthode qu’on peut utiliser avec un type de
données, il suffit d’appeler la fonction dir() en lui passant un objet en argument.
Note : Les méthodes commençant et finissant par __ (un double underscore) sont
appelées des “méthodes magiques”. Ces méthodes fournissent des fonctionnalités
syntaxiques spéciales ou font des choses spéciales. Généralement, on ne va pas les
appeler directement.
On va également pouvoir utiliser les méthodes classiques suivantes sur des objets
Python de
type str : capitalize(), casefold(), center(), count(), encode(), endswith(), expandtabs(), find(), format
(), format_map(), index(), isalnum(), isalpha(), isascii(), isdecimal(), isdigit(), isidentifier(), islower(),
isnumeric(), isprintable(), isspace(), istitle(), isupper(), join(), ljust(), lower(), lstrip(), maketrans(), pa
rtition(), replace(), rfind(), rindex(), rjust(), rpartition(), rsplit(), rstrip(), split(), splitlines(), startswith(
), strip(), swapcase(), title(), translate(), upper(), zfill().
Attention : la chaîne à laquelle on applique cette méthode est celle qui servira de
séparateur (un ou plusieurs caractères) ; l’argument transmis est la liste des chaînes à
rassembler. Si on lui passe un dictionnaire en argument, les valeurs retournées seront les
clefs du dictionnaire.
On peut également utiliser les expressions formatées en utilisant des noms de variable et
en préfixant notre chaine avec la lettre f.
Nous allons également pouvoir utiliser les méthodes suivantes avec les
listes : append(), clear(), copy(), count(), extend(), index(), insert(), pop(), remove(), reverse(), sort.
Nous allons également pouvoir utiliser les méthodes suivantes avec ce type de
données : clear(), copy(), fromkeys(), get(), items(), keys(), pop(), popitem(), setdefault(), update(),
values.
En Python, on peut distinguer trois grandes catégories de module en les classant selon
leur éditeur :
Les modules standards qui ne font pas partie du langage en soi mais sont
intégrés automatiquement par Python ;
Les modules développés par des développeurs externes qu’on va pouvoir utiliser ;
Les modules qu’on va développer nous mêmes.
Dans tous les cas, la procédure à suivre pour utiliser un mode sera la même. Nous allons
commencer par décrire cette procédure puis nous étudierons dans la suite de cette
partie quelques modules standards qu’il convient de connaitre.
Importer un module
Un programme Python va généralement être composé d’un script principal qui va
importer différents modules (c’est-à-dire différents fichiers Python) pour pouvoir les
utiliser.
Pour importer un module, on utilise la syntaxe import nom-de-mon-module. Pour utiliser les
éléments du module dans notre script, il faudra préfixer le nom de ces éléments par le
nom du module et un point. Cela permet d’éviter les conflits dans le cas où on aurait
défini des éléments de même nom que ceux disponibles dans le module.
Pour comprendre comment cela fonctionne en pratique, je vous invite à ouvrir votre
éditeur de texte et à créer un fichier qu’on va appeler “bonjour”. Notre fichier va contenir
une variable et une fonction comme ceci :
Ensuite, enregistrez le dans le dossier Python que vous devriez avoir créé au début de ce
cours et qui devrait se trouver sur votre bureau.
Dès que tout cela est fait, on peut retourner dans le terminal ou l’invite de commande.
Ne lancez pas l’interpréteur pour le moment et pensez bien à définir le dossier Python
comme dossier de référence pour la recherche de fichiers Python si ça n’a pas été fait au
début de ce cours.
On peut ensuite lancer l’interpréteur Python. Ici, il faut imaginer que notre terminal /
invite de commande représente le script principal de notre programme. On va importer
notre module dans ce script principal grâce à l’instruction import bonjour.
Lorsque l’interprète rencontre une instruction import, il importe le module s’il est présent
dans le path (le chemin de recherche). Pour rappel, le path ou chemin de recherche est
une liste de répertoires dans lesquels l’interpréteur cherche avant d’importer un module.
Pour être tout à fait précis, lorsqu’on importe un module, l’interpréteur Python le
recherche dans différents répertoires selon l’ordre suivant :
1. Le répertoire courant ;
2. Si le module est introuvable, Python recherche ensuite chaque répertoire listé
dans la variable shell PYTHONPATH ;
3. Si tout échoue, Python vérifie le chemin par défaut. Sous UNIX, ce chemin par
défaut est normalement /usr/local/lib/python/.
Dès que notre module est importé, on va pouvoir accéder tout simplement aux variables
et fonctions de notre module depuis notre script en préfixant le nom des éléments du
module par le nom du module et un point comme ceci :
Note : on ne peut importer un module qu’une fois dans un script. Si vous testez le code
ci-dessus et si vous aviez déjà importé le module précédemment, il faudra que vous
quittiez l’interpréteur et que vous le relanciez pour que tout fonctionne. Pour quitter
l’interpréteur, vous pouvez utiliser l’instruction quit().
Dans le cas où on n’importe que certains éléments depuis un module, il ne faudra pas
ensuite préfixer le nom des éléments par le nom du module pour les utiliser dans notre
script principal.
Pour faire cela, on peut utiliser la fonction dir() qui renvoie la liste de toutes les fonctions
et variables d’un module.
Comme vous pouvez le voir, tout fichier Python possède par défaut des éléments de
configuration.
Les modules Python standards
Comme je vous l’ai dit au début de cette leçon, nous importerons bien souvent des
modules créés par d’autres développeurs ou des modules mis à notre disposition par
Python lui même.
En effet, il existe un grand nombre de modules préconçus et prêts à l’emploi qui sont
fournis d’office avec Python. Ces modules vont étendre le langage et nous permettre de
réaliser toutes sortes d’opérations notamment grâce aux fonctions qu’ils nous
fournissent.
Pour importer un module Python, nous allons à nouveau tout simplement utiliser une
instruction import comme si on importait l’un de nos modules.
Le module math
Le module math nous fournit un accès à de nombreuses fonctions permettant de réaliser
des opérations mathématiques comme le calcul d’un sinus, cosinus, d’une tangente, d’un
logarithme ou d’une exponentielle.
Pour obtenir la liste complète des fonctions et constantes de ce module, je vous invite à
consulter la documentation.
Le module random
Le module random nous fournit des outils pour générer des nombres pseudo-aléatoires
de différentes façons.
Le module statistics
Le module statistics nous fournit des outils nous permettant d’effectuer des calculs de
statistiques peu complexes, comme des calculs de moyenne, de médiane ou de variance.
Python distingue deux sorte d’objets date et time : les objets “avisés” et les objets
“naifs”. Un objet avisé est un objet qui possède suffisamment de connaissances internes
pour se situer de façon relative par rapport à d’autres objets avisés. Un objet naïf ne
comporte pas assez d’informations pour se situer sans ambiguïté par rapport à d’autres
objets date ou time.
Ici, vous pouvez tenir que les objets avisés datetime et time ont un attribut optionnel
d’information sur le fuseau horaire nommé tzinfo. Cet attribut ou objet tzinfo capture
l’information à propos du décalage avec le temps UTC, le nom du fuseau horaire, et si
l’heure d’été est effective.
Voici quelques manipulations courantes qu’on va pouvoir effectuer à partir des classes
du module datetime :
Pour une référence complète sur le module Python datetime, je vous invite à consulter
la documentation.
L’epoch correspond au point de départ du temps utilisé comme référence par votre OS.
Il dépend donc de la plate-forme. Pour Unix, epoch est le 1er janvier 1970 à 00:00:00
(UTC). Pour savoir comment est définie epoch sur une plate-forme donnée, il suffit
d’appeler time.gmtime(0).
8 tm_isdst 0, 1 ou -1
Directive Signification
%Z Nom du fuseau horaire (pas de caractères s’il n’y a pas de fuseau horaire).
%% Un caractère '%' littéral.
Les calendriers sont des objets très spécifiques qu’on ne manipule pas tous les jours et je
n’irai donc pas plus loin dans la présentation de ce module. Si vous avez besoin un jour
de créer un calendrier, sachez simplement que ce module existe. Vous pourrez trouver
toutes les informations relatives aux calendriers Python sur la documentation officielle
du module.
Les expressions régulières sont des schémas ou des motifs utilisés pour effectuer des
recherches et des remplacements dans des chaines de caractères.
Ces schémas ou motifs sont tout simplement des séquences de caractères dont certains
vont disposer de significations spéciales et qui vont nous servir de schéma de recherche.
On les appelle également des masques de recherche.
La syntaxe des expressions rationnelles
Une expression rationnelle est représentée par un motif de recherche. Ce motif de
recherche va pouvoir être comparée à une ou plusieurs chaines pour déterminer les
correspondances.
Ces caractères spéciaux font toute la puissance des expressions rationnelles puisqu’ils
nous permettent de créer des motifs de recherche très précis et complexes.
Le premier caractère à connaitre est l’antislash \ qui est utilisé par les expressions
rationnelles pour donner un sens spécial à certains caractères normaux ou au contraire
pour neutraliser (“échapper”) le sens d’un caractère spécial et nous permettre de
rechercher le caractère en soi sans qu’il exprime son sens spécial.
Ici, nous sommes déjà confrontés à un souci puisque Python utilise également l’antislash
comme caractère d’échappement. Pour rechercher un antislash littéral avec une
expression régulière il faudrait donc écrire \\\\ puisque chaque antislash doit être
représenté par \\ dans une chaine littérale Python et que pour rechercher un antislash
(qui est un caractère spécial) dans une expression rationnelle il faut écrire \\ (le premier
antislash servant de caractère d’échappement pour rechercher le deuxième).
Pour éviter ce genre de situation, on utilisera une notation Python de chaines brutes
plutôt que littérales. Pour cela, il suffira de préfixer la chaine avec un r.
Ensuite, vous devez savoir que certains caractères ne vont avoir un sens spécial qu’en
dehors de ce qu’on appelle des classes de caractères ou vont pouvoir avoir des sens
spéciaux différents selon qu’ils soient dans des classes de caractères ou en dehors.
Commençons déjà par comprendre ce qu’est une classe de caractères.
Les classes de caractères
Les classes de caractères vont nous permettre de fournir différents choix de
correspondance pour un caractère en spécifiant un ensemble de caractères qui vont
pouvoir être trouvés.
En d’autres termes, elles vont nous permettre de rechercher n’importe quel caractère
d’une chaine qui fait partie de la classe de caractères fournie dans le masque, ce qui va
rendre les expressions régulières déjà beaucoup plus puissantes et flexibles qu’une
recherche classique.
Pour déclarer une classe de caractères dans notre masque de recherche, nous allons
utiliser une paire de crochets [ ] qui vont nous permettre de délimiter la classe en
question.
Dans le langage des expressions régulières, de nombreux caractères vont avoir une
signification spéciale et vont nous permettre de signifier qu’on recherche tel caractères
ou telle séquence de caractères un certain nombre de fois ou à une certaine place dans
une chaine.
Au sein des classes de caractères, seuls les caractères suivants possèdent une
signification spéciale :
Métacaractère Description
deux là).
Notez qu’il faudra également protéger les caractères de classe (les crochets) si on
souhaite les inclure pour les rechercher dans une classe de caractères car dans le cas
contraire on n’aurait aucun moyen de déterminer si le caractère “]” est recherché ou si la
classe est refermée par exemple.
La syntaxe des classes abrégées utilise l’antislash suivi d’un caractère “normal” afin de lui
donner dans un cas une signification spéciale. Les classes abrégées les plus intéressantes
sont les suivantes (faites bien attention aux emplois de majuscules et de minuscules
ici !) :
Classe
Description
abrégée
à [^a-zA- Z0-9_]
* 0, 1 ou plusieurs occurrences
+ 1 ou plusieurs occurrences
? 0 ou 1 occurrence
Note : les expressions rationnelles possèdent également des éléments de syntaxe plus
complexes comme les assertions que nous n’étudierons pas dans ce cours.
Le module Python re et ses fonctions
Maintenant que nous avons (très rapidement) parcouru les éléments de syntaxe
principaux des expressions régulières, il est temps de les utiliser avec le module re de
manière pratique.
Une expression rationnelle en soi n’est qu’un motif. Nous allons donc utiliser des
fonctions qui vont permettre d’effectuer des recherches de motifs dans des chaines, des
remplacements, etc.
Les variables sont très pratiques car elles vont nous permettre de manipuler nos
données très facilement en Python. Cependant, elles ne vont pas toujours être les outils
les mieux adaptés pour stocker des données.
En effet, le gros “défaut” des variables est qu’elles ne peuvent stocker une information
que temporairement (au mieux le temps de l’exécution d’un script).
Or, dans certains cas, on aimerait enregistrer des informations de manière définitives,
notamment lorsqu’il s’agit d’informations envoyées par les utilisateurs.
De plus, les variables ne sont pas adaptées pour le stockage de nombreuses données.
De manière générale, les bases de données sont plus puissantes et plus performantes
que le recours aux fichiers pour stocker des informations mais la gestion d’une base de
données est beaucoup plus complexe que l’utilisation de fichiers.
Nous utiliserons donc les fichiers pour des programmes aux ambitions plus modestes et
lorsqu’on n’aura pas de trop grosses quantités d’informations à stocker et au contraire
les bases de données lorsque le besoin d’une structure plus robuste se fera sentir.
Mode
Description
d’ouverture
fichier.
Notez qu’on va également pouvoir rajouter une autre lettre derrière le mode pour
définir si le fichier doit être ouvert en mode texte (lettre t, valeur par défaut ou en mode
binaire (lettre b). Dans ce cours, nous nous concentrerons sur le mode texte et
n’aborderons pas le mode binaire, généralement moins utilisé.
Fermer un fichier
Une fois qu’on a terminé de manipuler un fichier, il est considéré comme une bonne
pratique de le fumer. Cela évite d’utiliser des ressources inutilement et d’obtenir certains
comportements inattendus.
Le curseur ou pointeur est l’endroit dans un fichier à partir duquel une opération va être
faite. Pour donner un exemple concret, le curseur dans un document Word, dans un
champ de formulaire ou lorsque vous effectuez une recherche Google ou tapez une URL
dans votre navigateur correspond à la barre clignotante.
Ce curseur indique l’emplacement à partir duquel vous allez écrire votre requête ou
supprimer un caractère, etc. Le curseur dans les fichiers va être exactement la même
chose à la différence qu’ici on ne peut pas le voir visuellement.
Le mode d’ouverture choisi va être la première chose qui va influer sur la position du
pointeur. En effet, selon le mode choisi, le pointeur de fichier va se situer à une place
différente. Ensuite, il faut savoir que certaines méthodes vont déplacer ce curseur lors de
leur exécution, comme les méthodes de lecture du fichier par exemple.
Pour connaitre la place du pointeur interne dans un fichier et déplacer ce pointeur, nous
allons pouvoir utiliser les méthodes tell() et seek().
De plus, notez bien que les données seront écrites à partir de la position du curseur
interne et que si celui-ci est situé au début ou au milieu du fichier les nouvelles données
écraseront les anciennes.
Pour ne lire qu’une partie d’un fichier, on peut passer un nombre en argument
à read() qui lui indiquera combien de caractères lire à partir de la position courante du
pointeur interne.
Enfin, pour ne lire qu’une ligne d’un fichier, on peut utiliser la méthode readline().
On peut effectuer différentes opérations sur un fichier à la suite comme par exemple le
créer, écrire dedans puis lire les informations qu’il contient avec la syntaxe suivante.
Attention à la position du curseur !
On peut également ouvrir un fichier et écrire dedans puis le refermer et le réouvrir plus
tard pour lire les informations qu’on contient. Dans l’exemple ci-dessous, je n’utilise
plus with ni as. Je place les informations d’ouverture dans une variable. Il ne faudra pas
oublier de fermer le fichier manuellement dans ce cas avec close() dès nos opérations
terminées.
On va pouvoir utiliser cette fonction pour vérifier si un fichier existe et le créer si ce n’est
pas le cas :
Supprimer un fichier
Pour supprimer un fichier, on peut utiliser la fonction remove() du module Python os. On
va passer le chemin du fichier à supprimer en argument de celle-ci.
Ici, on teste si le fichier a bien été supprimé en observant si exists() renvoie bien False ou
pas (note l’utilisation de l’opérateur logique inverse not en début de condition if).
Présentation de JSON
JSON (JavaScript Object Notation) est un format d’échange de données dont la syntaxe
s’inspire des objets littéraux JavaScript bien que JSON n’appartienne pas au JavaScript.
JSON peut représenter des nombres, des booléens, des chaînes, la valeur null, des
séquences de valeurs ordonnées, des objets, etc. JSON ne représente pas nativement
des types de données plus complexes tels que des fonctions, des expressions régulières,
des dates, etc.
Une collection de paires nom / valeur. Dans les différentes langages, ce type de
structure peut s’appeler objet, enregistrement, dictionnaire, table de hachage,
liste à clé ou tableau associatif.
Une liste ordonnée de valeurs. Dans la plupart des langages, c’est ce qu’on va
appeler tableau, liste, vecteur ou séquence.
En JSON, ces deux structures se retrouvent sous les formes suivantes :
Lorsqu’on encode des données au format JSON, on dit également qu’on “sérialise” les
données. Ce terme fait référence à la transformation de données en une série d’octets.
Le processus inverse de décodage de données encodées au format JSON est également
appelé “désérialisation”.
Les objets Python vont être convertis selon la table d’équivalence suivante :
Python JSON
dict object
list, tuple array
str string
int, float number
True true
False false
None null
Comme les erreurs de syntaxe sont des erreurs que nous avons faites, nous pouvons les
corriger directement et c’est ce qu’on s’efforcera à faire et modifiant nos scripts. Pour les
autres erreurs, en revanche, il va falloir mettre en place un système de gestion d’erreurs
qui indiquera au Python quoi faire si telle ou telle erreur est rencontrée.
Il est essentiel de fournir une gestion des erreurs d’environnement afin de garantir que
le script ne plante pas dans certaines situations et pour garantir l’intégrité et la sécurité
des données ainsi qu’une bonne expérience pour l’utilisateur qui n’a pas envie de voir
des messages d’erreur Python.
En Python, nous allons pouvoir intercepter certaines erreurs pour les prendre en charge
nous mêmes et pour décider si le script doit continuer de s’exécuter ou pas.
Si vous essayez de déclencher des erreurs manuellement, vous pouvez constater que
Python analyse le type d’erreur et renvoie un message différent selon l’erreur détectée :
Ici, nous avons trois types d’exceptions différentes : une exception NameError, une
exception ZeroDivisionError et une exception TypeError. Comment fait Python pour
analyser les types d’erreurs et renvoyer des messages différents en fonction ?
En fait, vous devez savoir que Python possède de nombreuses classes d’exceptions
natives et que toute exception est une instance (un objet) créé à partir d’une classe
exception.
Afin de bien comprendre la hiérarchie des classes d’exceptions, vous pouvez retenir que
la classe d’exception de base pour les exceptions natives est BaseException. Toutes les
autres classes d’exception vont dériver de cette classe. Ensuite, nous avons également
quatre autres classes d’exception de base (qui dérivent de BaseException) :
Pour la liste complète des classes d’exception, vous pouvez consulter la documentation.
Imaginons par exemple un script qui demande à un utilisateur d’envoyer deux nombres
grâce à la fonction input() qui permet de recevoir des données d’utilisateurs externes.
Le but de notre script va être de calculer le quotient de ces deux nombres entiers. Ici, on
peut déjà anticiper des cas d’erreurs qui peuvent se présenter : l’utilisateur peut nous
envoyer autre chose qu’un nombre entier ou peut nous envoyer un dénominateur égal à
0, ce qui est interdit en mathématique.
On va vouloir gérer ces deux cas exceptionnels. Pour cela, on va pouvoir utiliser deux
blocs try et except comme cela :
Ici, on place le code à tester à l’intérieur du bloc try. Si aucune erreur n’est détectée par
Python lors de l’exécution du code, c’est-à-dire si aucun objet exception n’est créé, ce
qui se situe dans la clause except va être ignoré.
Notre code ci-dessus est cependant loin d’être optimise car notre clause except est bien
trop large. Lorsqu’on gère les exceptions manuellement, on voudra toujours apporter la
gestion la plus fine possible pour éviter de capturer toutes les erreurs n’importe
comment.
Pour cela, nous allons préciser le type d’erreur qu’une instruction except doit intercepter.
Si on souhaite gérer plusieurs types d’exceptions, on pourra préciser autant
d’instructions except qu’on le souhaite à la suite d’un try.
Notez que rien ne nous empêche de préciser un except sans classe à la fin si on souhaite
absolument donner des ordres dans le cas où Python capturerait tout autre type
d’exceptions que ceux correspondant aux clauses except précédentes.
La clause else
Nous allons également pouvoir ajouter une clause else en fin d’instruction try… except. Le
code contenu dans cette clause sera exécuté dans le cas où aucune exception n’a été
levée par la clause try.
Il est considéré comme une bonne pratique de placer le code “non problématique” dans
la clause else plutôt que dans la clause try.
La clause finally
La dernière clause à connaitre est la clause fin ally. Le code qu’elle contient sera exécuté
dans tous les cas, qu’une exception ait été levée par la clause try ou pas.
Cette clause va s’avérer très utile lorsqu’on voudra terminer certaines opérations (fermer
un fichier par exemple) quel que soit l’état du script.
Pour cela, nous allons toujours créer des classes à partir de la classe de base Exception.
Définir nos propres exceptions va s’avérer être une fonctionnalité très utile notamment
dans le cas où on souhaite distribuer un module Python et que certaines de nos
fonctions peuvent déclencher des exceptions non prises en charge par les classes
d’exception standard Python.