Fondamentaux du langage Python 3
Fondamentaux du langage Python 3
https ://[Link]
2
Page
i
Chapitre 2
2.1 w2-s1-c1-accents
Caractères accentués
Ce complément expose quelques bases concernant les caractères accentués, et notamment les précautions
à prendre pour pouvoir en insérer dans un programme Python. Nous allons voir que cette question, assez
scabreuse, dépasse très largement le cadre de Python.
Avec Unicode, on a cassé le modèle un caractère == un octet. Aussi en Python 3, lorsqu’il s’agit de
manipuler des données provenant de diverses sources de données :
— le type byte est approprié si vous voulez charger en mémoire les données binaires brutes, sous
forme d’octets donc ;
— le type str est approprié pour représenter une chaîne de caractères - qui, à nouveau ne sont pas
forcément des octets ;
— on passe de l’un à l’autre de ces types par des opérations d’encodage et décodage, comme illustré
ci-dessous ;
— et pour toutes les opérations d’encodage et décodage, il est nécessaire de connaître l’encodage
utilisé.
1
MOOC Python 3 Semaine 2, Séquence 1
On peut appeler les méthodes encode et decode sans préciser l’encodage (dans ce cas Python choisit
l’encodage par défaut sur votre système). Cela dit, il est de loin préférable d’être explicite et de choisir
son encodage. En cas de doute, il est recommandé de spécifier explicitement utf-8, qui se généralise au
détriment d’encodages anciens comme cp1252 (Windows) et iso8859-*, que de laisser le système hôte
choisir pour vous.
Python 3 supporte Unicode par défaut. Vous pouvez donc, maintenant, utiliser sans aucun risque des
accents ou des cédilles dans vos chaînes de caractères. Il faut cependant faire attention à deux choses :
— Python supporte Unicode, donc tous les caractères du monde, mais les ordinateurs n’ont pas for-
cément les polices de caractères nécessaires pour afficher ces caractères ;
— Python permet d’utiliser des caractères Unicode pour les noms de variables, mais nous vous re-
commandons dans toute la mesure du possible d’écrire votre code en anglais, comme c’est le cas
pour la quasi-totalité du code que vous serez amenés à utiliser sous forme de bibliothèques. Ceci est
particulièrement important pour les noms de lignes et de colonnes dans un dataset afin de faciliter
les transferts entre logiciels, la majorité des logiciels n’acceptant pas les accents et cédilles dans les
noms de variables.
Ainsi, il faut bien distinguer les chaînes de caractères qui doivent par nature être adaptées au langage
des utilisateurs du programme, et le code source qui lui est destiné aux programmeurs et qui doit donc
éviter d’utiliser autre chose que de l’anglais.
Cela étant dit, si vous devez vraiment mettre des accents dans vos sources, voici ce qu’il faut savoir.
Noms de variables
w2-s1-c1-accents 2
MOOC Python 3 Semaine 2, Séquence 1
— S’il n’était pas possible en Python 2 d’utiliser un caractère accentué dans un nom de variable (ou
d’un identificateur au sens large), cela est à présent permis en Python 3 :
[1]: # pas recommandé, mais autorisé par le langage
nb_élèves = 12
[2]: 0.7071067811865476
— Je vous recommande toutefois de ne pas utiliser cette possibilité, si vous n’êtes pas extrêmement
familier avec les caractères Unicode.
— Enfin, pour être exhaustif, sachez que seule une partie des caractères Unicode sont autorisés dans ce
cadre, c’est heureux parce que les caractères comme, par exemple, l’espace non-sécable pourraient,
s’ils étaient autorisés, être la cause de milliers d’heures de debugging à frustration garantie :)
Pour les curieux, vous pouvez en savoir plus à cet endroit de la documentation officielle (en anglais).
Chaînes de caractères
— Vous pouvez naturellement mettre des accents dans les chaînes de caractères. Cela dit, les données
manipulées par un programme proviennent pour l’essentiel de sources externes, comme une base de
données ou un formulaire Web, et donc le plus souvent pas directement du code source. Les chaînes
de caractères présentes dans du vrai code sont bien souvent limitées à des messages de logging, et
le plus souvent d’ailleurs en anglais, donc sans accent.
— Lorsque votre programme doit interagir avec les utilisateurs et qu’il doit donc parler leur langue,
c’est une bonne pratique de créer un fichier spécifique, que l’on appelle fichier de ressources, qui
contient toutes les chaînes de caractères spécifiques à une langue. Ainsi, la traduction de votre
programme consistera à simplement traduire ce fichier de ressources.
Commentaires
— Enfin on peut aussi bien sûr mettre dans les commentaires n’importe quel caractère Unicode, et
donc notamment des caractères accentués si on choisit malgré tout d’écrire le code en français.
Comme vous le savez, la mémoire - ou le disque - d’un ordinateur ne permet que de stocker des repré-
sentations binaires. Il n’y a donc pas de façon “naturelle” de représenter un caractère comme ‘A’, un
guillemet ou un point-virgule.
w2-s1-c1-accents 3
MOOC Python 3 Semaine 2, Séquence 1
On utilise pour cela un encodage, par exemple le code US-ASCII stipule, pour faire simple, qu’un ‘A’
est représenté par l’octet 65 qui s’écrit en binaire 01000001. Il se trouve qu’il existe plusieurs encodages,
bien sûr incompatibles, selon les systèmes et les langues. Vous trouverez plus de détails ci-dessous.
Le point important est que pour pouvoir ouvrir un fichier “proprement”, il faut bien entendu disposer
du contenu du fichier, mais il faut aussi connaître l’encodage qui a été utilisé pour l’écrire.
L’encodage ne concerne pas simplement les objets chaîne de caractères, mais également votre code source.
Python 3 considère que votre code source utilise par défaut l’encodage UTF-8. Nous vous conseillons de
conserver cet encodage qui est celui qui vous offrira le plus de flexibilité.
Vous pouvez malgré tout changer l’encodage de votre code source en faisant figurer dans vos fichiers, en
première ou deuxième ligne, une déclaration comme ceci :
# coding: <nom_de_l_encodage>
Notons que la première option est également interprétée par l’éditeur de texte Emacs pour utiliser le même
encodage. En dehors de l’utilisation d’Emacs, la deuxième option, plus simple et donc plus pythonique,
est à préférer.
Le nom UTF-8 fait référence à Unicode (ou pour être précis, à l’encodage le plus répandu parmi ceux qui
sont définis dans la norme Unicode, comme nous le verrons plus bas). Sur certains systèmes plus anciens
vous pourrez être amenés à utiliser un autre encodage. Pour déterminer la valeur à utiliser dans votre
cas précis vous pouvez faire dans l’interpréteur interactif :
Par exemple avec d’anciennes versions de Windows (en principe de plus en plus rares) vous pouvez être
amenés à écrire :
# coding: cp1252
La syntaxe de la ligne coding est précisée dans cette documentation et dans le PEP 263.
Le grand malentendu
Si je vous envoie un fichier contenant du français encodé avec, disons, ISO/IEC 8859-15 - a.k.a. Latin-9 ;
vous pouvez voir dans la table qu’un caractère ‘e’ va être matérialisé dans mon fichier par un octet ‘0xA4’,
soit 164.
w2-s1-c1-accents 4
MOOC Python 3 Semaine 2, Séquence 1
Imaginez maintenant que vous essayez d’ouvrir ce même fichier depuis un vieil ordinateur Windows
configuré pour le français. Si on ne lui donne aucune indication sur l’encodage, le programme qui va lire
ce fichier sur Windows va utiliser l’encodage par défaut du système, c’est-à-dire CP1252. Comme vous le
voyez dans cette table, l’octet ‘0xA4’ correspond au caractère et c’est ça que vous allez voir à la place
de e.
Contrairement à ce qu’on pourrait espérer, ce type de problème ne peut pas se régler en ajoutant une
balise # coding: <nom_de_l_encodage>, qui n’agit que sur l’encodage utilisé pour lire le fichier source
en question (celui qui contient la balise).
Pour régler correctement ce type de problème, il vous faut préciser explicitement l’encodage à utiliser
pour décoder le fichier. Et donc avoir un moyen fiable de déterminer cet encodage ; ce qui n’est pas
toujours aisé d’ailleurs, mais c’est une autre discussion malheureusement. Ce qui signifie que pour être
totalement propre, il faut pouvoir préciser explicitement le paramètre encoding à l’appel de toutes les
méthodes qui sont susceptibles d’en avoir besoin.
Lorsque le producteur (le programme qui écrit le fichier) et le consommateur (le programme qui le lit)
tournent dans le même ordinateur, tout fonctionne bien - en général - parce que les deux programmes se
ramènent à l’encodage défini comme l’encodage par défaut.
Il y a toutefois une limite, si vous utilisez un Linux configuré de manière minimale, il se peut qu’il utilise
par défaut l’encodage US-ASCII - voir plus bas - qui étant très ancien ne “connaît” pas un simple é, ni a
fortiori €. Pour écrire du français, il faut donc au minimum que l’encodage par défaut de votre ordinateur
contienne les caractères français, comme par exemple :
À nouveau dans cette liste, il faut clairement préférer UTF-8 lorsque c’est possible.
Le code US-ASCII
Jusque dans les années 1980, les ordinateurs ne parlaient pour l’essentiel que l’anglais. La première vague
de standardisation avait créé l’encodage dit ASCII, ou encore US-ASCII voir par exemple ici, ou encore
en version longue ici.
Le code US-ASCII s’étend sur 128 valeurs, soit 7 bits, mais est le plus souvent implémenté sur un
octet pour préserver l’alignement, le dernier bit pouvant être utilisé par exemple pour ajouter un code
correcteur d’erreur - ce qui à l’époque des modems n’était pas superflu. Bref, la pratique courante était
alors de manipuler une chaîne de caractères comme un tableau d’octets.
Dans les années 1990, pour satisfaire les besoins des pays européens, ont été définis plusieurs encodages
alternatifs, connus sous le nom de ISO/IEC 8859-*, nommés aussi Latin-*. Idéalement, on aurait pu
et certainement dû définir un seul encodage pour représenter tous les nouveaux caractères, mais entre
toutes les langues européennes, le nombre de caractères à ajouter était substantiel, et cet encodage unifié
aurait largement dépassé 256 caractères différents, il n’aurait donc pas été possible de tout faire tenir
sur un octet.
w2-s1-c1-accents 5
MOOC Python 3 Semaine 2, Séquence 2
On a préféré préserver la “bonne propriété” du modèle un caractère == un octet, ceci afin de préserver
le code existant qui aurait sinon dû être retouché ou réécrit.
Dès lors il n’y avait pas d’autre choix que de définir plusieurs encodages distincts, par exemple,
pour le français on a utilisé à l’époque ISO/IEC 8859-1 (Latin-1), pour le russe ISO/IEC 5589-5
(Latin/Cyrillic).
À ce stade, le ver était dans le fruit. Depuis cette époque pour ouvrir un fichier il faut connaître son
encodage.
Unicode
Lorsque l’on a ensuite cherché à manipuler aussi les langues asiatiques, il a de toute façon fallu définir
de nouveaux encodages beaucoup plus larges. C’est ce qui a été fait par le standard Unicode qui définit
3 nouveaux encodages :
— UTF-8 : un encodage à taille variable, à base d’octets, qui maximise la compatibilité avec US-ASCII ;
— UTF-16 : un encodage à taille variable, à base de mots de 16 bits ;
— UTF-32 : un encodage à taille fixe, à base de mots de 32 bits ;
Ces 3 standards couvrent le même jeu de caractères (113 021 tout de même dans la dernière version).
Parmi ceux-ci le plus utilisé est certainement UTF-8. Un texte ne contenant que des caractères du code
US-ASCII initial peut être lu avec l’encodage UTF-8.
Pour être enfin tout à fait exhaustif, si on sait qu’un fichier est au format Unicode, on peut déterminer
quel est l’encodage qu’il utilise, en se basant sur les 4 premiers octets du document. Ainsi dans ce
cas particulier (lorsqu’on est sûr qu’un document utilise un des trois encodages Unicode) il n’est plus
nécessaire de connaître son encodage de manière “externe”.
2.2 w2-s2-c1-outils-chaines
Lire la documentation
Même après des années de pratique, il est difficile de se souvenir de toutes les méthodes travaillant sur
les chaînes de caractères. Aussi il est toujours utile de recourir à la documentation embarquée
[ ]: help(str)
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Nous allons tenter ici de citer les méthodes les plus utilisées. Nous n’avons le temps que de les utiliser de
manière très simple, mais bien souvent il est possible de passer en argument des options permettant de
ne travailler que sur une sous-chaîne, ou sur la première ou dernière occurrence d’une sous-chaîne. Nous
vous renvoyons à la documentation pour obtenir toutes les précisions utiles.
w2-s2-c1-outils-chaines 6
MOOC Python 3 Semaine 2, Séquence 2
Les méthodes split et join permettent de découper une chaîne selon un séparateur pour obtenir une
liste, et à l’inverse de reconstruire une chaîne à partir d’une liste.
[1]: 'abc=:=def=:=ghi=:=jkl'.split('=:=')
Et à l’inverse :
[2]: 'abc=:=def=:=ghi=:=jkl'
Attention toutefois si le séparateur est un terminateur, la liste résultat contient alors une dernière chaîne
vide. En pratique, on utilisera la méthode strip, que nous allons voir ci-dessous, avant la méthode split
pour éviter ce problème.
[3]: 'abc;def;ghi;jkl;'.split(';')
[4]: 'abc;def;ghi;jkl;'
Remplacement : replace
replace est très pratique pour remplacer une sous-chaîne par une autre, avec une limite éventuelle sur
le nombre de remplacements :
[5]: 'zoodefzoodefzoodef'
[6]: 'zoodefzoodefabcdef'
w2-s2-c1-outils-chaines 7
MOOC Python 3 Semaine 2, Séquence 2
Nettoyage : strip
On pourrait par exemple utiliser replace pour enlever les espaces dans une chaîne, ce qui peut être utile
pour “nettoyer” comme ceci :
[8]: 'abc:def:ghi'
Toutefois bien souvent on préfère utiliser strip qui ne s’occupe que du début et de la fin de la chaîne,
et gère aussi les tabulations et autres retour à la ligne :
[9]: " \tune chaîne avec des trucs qui dépassent \n".strip()
On peut appliquer strip avant split pour éviter le problème du dernier élément vide :
[10]: 'abc;def;ghi;jkl;'.strip(';').split(';')
Plusieurs outils permettent de chercher une sous-chaîne. Il existe find qui renvoie le plus petit index où
on trouve la sous-chaîne :
[11]: 3
[12]: -1
[13]: 13
w2-s2-c1-outils-chaines 8
MOOC Python 3 Semaine 2, Séquence 2
[14]: 'f'
La méthode index se comporte comme find, mais en cas d’absence elle lève une exception (nous verrons
ce concept plus tard) plutôt que de renvoyer -1 :
[15]: "abcdefcdefghefghijk".index("def")
[15]: 3
[16]: try:
"abcdefcdefghefghijk".index("zoo")
except Exception as e:
print("OOPS", type(e), e)
Mais le plus simple pour chercher si une sous-chaîne est dans une autre chaîne est d’utiliser l’instruction
in sur laquelle nous reviendrons lorsque nous parlerons des séquences :
[17]: True
[18]: "abcdefcdefghefghijk".count("ef")
[18]: 3
[19]: "abcdefcdefghefghijk".startswith("abcd")
[19]: True
[20]: "abcdefcdefghefghijk".endswith("ghijk")
[20]: True
[Link](sous_chaine) ⇐⇒ [Link](sous_chaine) == 0
Changement de casse
w2-s2-c1-outils-chaines 9
MOOC Python 3 Semaine 2, Séquence 2
2.3 w2-s2-c2-formatage
On désigne par formatage les outils qui permettent d’obtenir une présentation fine des résultats, que ce
soit pour améliorer la lisibilité lorsqu’on s’adresse à des humains, ou pour respecter la syntaxe d’un outil
auquel on veut passer les données pour un traitement ultérieur.
La fonction print
Nous avons jusqu’à maintenant presque toujours utilisé la fonction print pour afficher nos résultats.
Comme on l’a vu, celle-ci réalise un formatage sommaire : elle insère un espace entre les valeurs qui lui
sont passées.
1 a (12+4j)
La seule subtilité notable concernant print est que, par défaut, elle ajoute un saut de ligne à la fin. Pour
éviter ce comportement, on peut passer à la fonction un argument end, qui sera inséré au lieu du saut
de ligne. Ainsi par exemple :
w2-s2-c2-formatage 10
MOOC Python 3 Semaine 2, Séquence 2
Il faut remarquer aussi que print est capable d’imprimer n’importe quel objet. Nous l’avons déjà fait
avec les listes et les tuples, voici par exemple un module :
En anticipant un peu, voici comment print présente les instances de classe (ne vous inquiétez pas, nous
apprendrons dans une semaine ultérieure ce que sont les classes et les instances).
— d’une part, il peut être nécessaire de formater une chaîne de caractères sans nécessairement vouloir
l’imprimer, ou en tout cas pas immédiatement ;
— d’autre part, les espaces ajoutées peuvent être plus néfastes qu’utiles ;
— enfin, on peut avoir besoin de préciser un nombre de chiffres significatifs, ou de choisir comment
présenter une date.
C’est pourquoi il est plus courant de formater les chaînes - c’est-à-dire de calculer des chaînes en mémoire,
sans nécessairement les imprimer de suite, et c’est ce que nous allons étudier dans ce complément.
Les f-strings
Depuis la version 3.6 de Python, on peut utiliser les f-strings, le premier mécanisme de formatage que
nous étudierons. C’est le mécanisme de formatage le plus simple et le plus agréable à utiliser.
Je vous recommande tout de même de lire les sections à propos de format et de %, qui sont encore
massivement utilisées dans le code existant (surtout % d’ailleurs, bien que essentiellement obsolète).
w2-s2-c2-formatage 11
MOOC Python 3 Semaine 2, Séquence 2
Vous remarquez d’abord que la chaine commence par f", c’est bien sûr pour cela qu’on l’appelle un
f-string.
On peut bien entendu ajouter le f devant toutes les formes de strings, qu’ils commencent par ' ou " ou
''' ou """.
Ensuite vous remarquez que les zones délimitées entre {} sont remplacées. La logique d’un f-string, c’est
tout simplement de considérer l’intérieur d’un {} comme du code Python (une expression pour être
précis), de l’évaluer, et d’utiliser le résultat pour remplir le {}.
Ça veut dire, en clair, que je peux faire des calculs à l’intérieur des {}.
Nous allons en rester là pour la partie en niveau basique. Il nous reste à étudier comment chaque {} est
formaté (par exemple comment choisir le nombre de chiffres significatifs sur un flottant), ce point est
expliqué plus bas.
Comme vous le voyez, les f-strings fournissent une méthode très simple et expressive pour formater
des données dans des chaînes de caractère. Redisons-le pour être bien clair : un f-string ne réalise pas
d’impression, il faut donc le passer à print si l’impression est souhaitée.
La méthode format
Avant l’introduction des f-strings, la technique recommandée pour faire du formatage était d’utiliser la
méthode format qui est définie sur les objets str et qui s’utilise comme ceci :
Dans cet exemple le plus simple, les données sont affichées en lieu et place des {}, dans l’ordre où elles
sont fournies.
w2-s2-c2-formatage 12
MOOC Python 3 Semaine 2, Séquence 2
Cela convient bien lorsqu’on a peu de données. Si par la suite on veut changer l’ordre par exemple des
nom et prénom, on peut bien sûr échanger l’ordre des arguments passés à format, ou encore utiliser la
liaison par position, comme ceci :
Dans la pratique toutefois, cette forme est assez peu utile, on lui préfère souvent la liaison par nom qui
se présente comme ceci :
Petite digression : remarquez l’usage des parenthèses, qui me permettent de couper ma ligne en deux,
car sinon ce code serait trop long pour la PEP8 ; on s’efforce toujours de ne pas dépasser 80 caractères
de large, dans notre cas c’est utile notamment pour l’édition du cours au format PDF.
Reprenons : dans ce premier exemple de liaison par nom, nous avons délibérément utilisé des noms
différents pour les données externes et pour les noms apparaissant dans le format, pour bien illustrer
comment la liaison est résolue, mais on peut aussi bien faire tout simplement :
format a été en fait introduite assez tard dans Python, pour remplacer la technique que nous allons
présenter maintenant.
Étant donné le volume de code qui a été écrit avec l’opérateur %, il nous a semblé important d’introduire
brièvement cette construction ici. Vous ne devez cependant pas utiliser cet opérateur dans du code
moderne, la manière pythonique de formater les chaînes de caractères est le f-string.
Le principe de l’opérateur % est le suivant. On élabore comme ci-dessus un “format” c’est-à-dire le patron
de ce qui doit être rendu, auquel on passe des arguments pour “remplir” les trous. Voyons les exemples
de tout à l’heure avec l’opérateur % :
w2-s2-c2-formatage 13
MOOC Python 3 Semaine 2, Séquence 2
On pouvait également avec cet opérateur recourir à un mécanisme de liaison par nommage, en passant
par un dictionnaire. Pour anticiper un tout petit peu sur cette notion que nous verrons très bientôt, voici
comment
De retour aux f-strings et à la fonction format, il arrive qu’on ait besoin de spécifier plus finement la
façon dont une valeur doit être affichée ; cela se fait en précisant un format à l’intérieur des {} comme
ceci :
— à gauche du : vous pouvez mettre n’importe quelle expression (opérations arithmétiques, appels
de fonctions, …) ; bien sûr s’il n’y a pas de : tout ce qui est entre les {} constitue l’expression à
évaluer ;
— à droite du : vous pouvez préciser un format, onus allons en voir quelques exemples.
C’est typiquement le cas avec les valeurs flottantes pour lesquelles la précision de l’affichage vient au
détriment de la lisibilité.
[18]: # un f-string
f"2pi avec seulement 2 chiffres apres la virgule {2*pi:.2f}"
Vous remarquez que la façon de construire un format est la même pour les f-strings et pour format.
0 en début de nombre
Pour forcer un petit entier à s’afficher sur 4 caractères, avec des 0 ajoutés au début si nécessaire :
[19]: x = 15
f"{x:04d}"
[19]: '0015'
w2-s2-c2-formatage 14
MOOC Python 3 Semaine 2, Séquence 2
Ici on utilise le format d (toutes ces lettres d, f, g viennent des formats ancestraux de la libc comme
printf). Ici avec 04d on précise qu’on veut une sortie sur 4 caractères et qu’il faut remplir à gauche si
nécessaire avec des 0.
Largeur fixe
Dans certains cas, on a besoin d’afficher des données en colonnes de largeur fixe, on utilise pour cela les
formats < ^ et > pour afficher à gauche, au centre, ou à droite d’une zone de largeur fixe :
Voir aussi
Nous vous invitons à vous reporter à la documentation de format pour plus de détails sur les formats
disponibles, et notamment aux nombreux exemples qui y figurent.
2.4 w2-s2-c3-la-fonction-input
La fonction input
# NOTE:
# auto-exec-for-latex has used instead:
##########
nom_ville = 'Paris'
##########
[2]: print(f"nom_ville={nom_ville}")
w2-s2-c3-la-fonction-input 15
MOOC Python 3 Semaine 2, Séquence 2
nom_ville=Paris
Notez bien que input renvoie toujours une chaîne de caractères (str). C’est assez évident, mais il est
très facile de l’oublier et de passer cette chaîne directement à une fonction qui s’attend à recevoir, par
exemple, un nombre entier, auquel cas les choses se passent mal :
Dans ce cas il faut appeler la fonction int pour convertir le résultat en un entier :
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Limitations
Cette fonction peut être utile pour vos premiers pas en Python.
En pratique toutefois, on utilise assez peu cette fonction, car les applications “réelles” viennent avec
leur propre interface utilisateur, souvent graphique, et disposent donc d’autres moyens que celui-ci pour
interagir avec l’utilisateur.
Les applications destinées à fonctionner dans un terminal, quant à elles, reçoivent traditionnellement leurs
données de la ligne de commande. C’est le propos du module argparse que nous avons déjà rencontré
en première semaine.
2.5 w2-s2-c4-expressions-regulieres
Avertissement
Après avoir joué ce cours plusieurs années de suite, l’expérience nous montre qu’il est difficile de trouver
le bon moment pour appréhender les expressions régulières.
D’un côté il s’agit de manipulations de chaînes de caractères, mais d’un autre cela nécessite de créer des
instances de classes, et donc d’avoir vu la programmation orientée objet. Du coup, les premières années
nous les avions étudiées tout à la fin du cours, ce qui avait pu créer une certaine frustration.
C’est pourquoi nous avons décidé à présent de les étudier très tôt, dans cette séquence consacrée aux
chaines de caractères. Les étudiants qui seraient décontenancés par ce contenu sont invités à y retourner
après la semaine 6, consacrée à la programmation objet.
w2-s2-c4-expressions-regulieres 16
MOOC Python 3 Semaine 2, Séquence 2
Il nous semble important de savoir que ces fonctionnalités existent dans le langage, le détail de leur
utilisation n’est toutefois pas critique, et on peut parfaitement faire l’impasse sur ce complément en
première lecture.
Une expression régulière est un objet mathématique permettant de décrire un ensemble de textes qui
possèdent des propriétés communes. Par exemple, s’il vous arrive d’utiliser un terminal, et que vous tapez
$ dir *.txt
(ou ls *.txt sur linux ou mac), vous utilisez l’expression régulière *.txt qui désigne tous les fichiers
dont le nom se termine par .txt. On dit que l’expression régulière filtre toutes les chaînes qui se terminent
par .txt (l’expression anglaise consacrée est le pattern matching).
Le langage Perl a été le premier à populariser l’utilisation des expressions régulières en les supportant
nativement dans le langage, et non au travers d’une librairie. En python, les expressions régulières sont
disponibles de manière plus traditionnelle, via le module re (regular expressions) de la librairie standard.
Le propos de ce complément est de vous en donner une première introduction.
[1]: import re
Survol
Pour ceux qui ne souhaitent pas approfondir, voici un premier exemple ; on cherche à savoir si un objet
chaine est ou non de la forme *-*.txt, et si oui, à calculer la partie de la chaine qui remplace le * :
[4]: True
Le fait que l’objet match vaut None indique que la chaine n’est pas de la bonne forme (il manque un -
dans le nom) ; avec une autre chaine par contre :
[6]: False
Ici match est un objet, qui nous permet ensuite d’“extraire” les différentes parties, comme ceci :
[7]: match[1]
w2-s2-c4-expressions-regulieres 17
MOOC Python 3 Semaine 2, Séquence 2
[7]: 'abc'
[8]: match[2]
[8]: 'def'
Bien sûr on peut faire des choses beaucoup plus élaborées avec re, mais en première lecture cette
introduction doit vous suffire pour avoir une idée de ce qu’on peut faire avec les expressions régulières.
Synonymes
Avant d’aller plus loin signalons qu’on utilise indifféremment les termes pour désigner essentiellement la
même chose :
— expression régulière
— en anglais regular expression - d’où le nom du module dans import re
— en anglais raccourci regexp, c’est de facto devenu un nom commun
— en français on trouve aussi parfois le terme d’expression rationnelle, c’est plus rare et un peu pédant
— en anglais on utilise aussi facilement le terme de pattern
— qui du coup a été traduit en français par motif ; bon ça c’est d’un emploi assez rare.
Après selon les contextes ces termes peuvent être utilisés pour désigner des choses subtilement différentes
- par exemple pour distinguer la chaine qui spécifie un pattern de l’objet regexp qui en est déduit ; mais
à ce stade de la présentation on peut signaler tous ces termes et les assimiler en gros à la même notion.
Approfondissons à présent :
Dans un terminal, *.txt est une expression régulière très simple. Le module re fournit le moyen de
construire des expressions régulières très élaborées et plus puissantes que ce que supporte le terminal.
C’est pourquoi la syntaxe des regexps de re est un peu différente. Par exemple comme on vient de le voir,
pour filtrer la même famille de chaînes que *-*.txt avec le module re, il nous a fallu écrire l’expression
régulière sous une forme légèrement différente.
Je vous conseille d’avoir sous la main la documentation du module re pendant que vous lisez ce complé-
ment.
Avertissement
Dans ce complément nous serons amenés à utiliser des traits qui dépendent du LOCALE, c’est-à-dire,
pour faire simple, de la configuration de l’ordinateur vis-à-vis de la langue.
Tant que vous exécutez ceci dans le notebook sur la plateforme, en principe tout le monde verra exacte-
ment la même chose. Par contre, si vous faites tourner le même code sur votre ordinateur, il se peut que
vous obteniez des résultats légèrement différents.
Un exemple simple
findall
w2-s2-c4-expressions-regulieres 18
MOOC Python 3 Semaine 2, Séquence 2
On peut chercher tous les mots se terminant par a ou m dans une chaîne avec findall
Ce code permet de chercher toutes (findall) les occurrences de l’expression régulière, qui ici est définie
par la chaine :
r"\w*[am]\W"
digression : les raw-strings Pour anticiper un peu, signalons que cette façon de créer un chaine en la
préfixant par un r s’appelle une raw-string ; l’intérêt c’est de ne pas interpréter les backslashs \
sans raw-string
un newline
[12]: print(r"dans\nunraw-string")
dans\nunraw-string
Comme vous le voyez dans une chaine “normale” les caractères backslash ont une signification particu-
lière ; mais nous ce qu’on veut faire, quand on crée une expression régulière, c’est de laisser les backslashs
intacts, car c’est à la couche de regexp de les interpréter.
reprenons Nous verrons tout à l’heure comment fabriquer des expressions régulières plus en détail, mais
pour démystifier au moins celle-ci, on a mis bout à bout les morceaux suivants.
— \w* : on veut trouver une sous-chaîne qui commence par un nombre quelconque, y compris nul (*)
de caractères alphanumériques (\w). Ceci est défini en fonction de votre LOCALE, on y reviendra.
— [am] : immédiatement après, il nous faut trouver un caratère a ou m.
— \W : et enfin, il nous faut un caractère qui ne soit pas alphanumérique. Ceci est important puisqu’on
cherche les mots qui se terminent par un a ou un m, si on ne le mettait pas on obtiendrait ceci
w2-s2-c4-expressions-regulieres 19
MOOC Python 3 Semaine 2, Séquence 2
split
Une autre forme simple d’utilisation des regexps est [Link], qui fournit une fonctionnalité voisine de
[Link], mais ou les séparateurs sont exprimés comme une expression régulière
Ici l’expression régulière, qui bien sûr décrit le séparateur, est simplement \W+ c’est-à-dire toute suite
d’au moins un caractère non alphanumérique.
Nous avons donc là un moyen simple, et plus puissant que [Link], de couper un texte en mots.
sub
Une troisième méthode utilitaire est [Link] qui permet de remplacer les occurrences d’une regexp,
comme par exemple
Ici, l’expression régulière (le premier argument) contient un groupe : on a utilisé des parenthèses autour
du \w+. Le second argument est la chaîne de remplacement, dans laquelle on a fait référence au groupe
en écrivant \1, qui veut dire tout simplement “le premier groupe”.
Donc au final, l’effet de cet appel est d’entourer toutes les suites de caractères alphanumériques par X et
Y.
w2-s2-c4-expressions-regulieres 20
MOOC Python 3 Semaine 2, Séquence 2
Pourquoi un raw-string ?
En guise de digression, il n’y a aucune obligation à utiliser un raw-string, d’ailleurs on rappelle qu’il n’y
a pas de différence de nature entre un raw-string et une chaîne usuelle
Il se trouve que le backslash \ à l’intérieur des expressions régulières est d’un usage assez courant - on l’a
vu déjà plusieurs fois. C’est pourquoi on utilise fréquemment un raw-string pour décrire une expression
régulière. On rappelle que le raw-string désactive l’interprétation des \ à l’intérieur de la chaîne, par
exemple, \t est interprété comme un caractère de tabulation dans une chaine usuelle. Sans raw-string,
il faut doubler tous les \ pour qu’il n’y ait pas d’interprétation.
Un deuxième exemple
Nous allons maintenant voir comment on peut d’abord vérifier si une chaîne est conforme au critère défini
par l’expression régulière, mais aussi extraire les morceaux de la chaîne qui correspondent aux différentes
parties de l’expression.
Pour cela, supposons qu’on s’intéresse aux chaînes qui comportent 5 parties, une suite de chiffres, une
suite de lettres, des chiffres à nouveau, des lettres et enfin de nouveau des chiffres.
match
Pour commencer, voyons que l’on peut facilement vérifier si une chaîne vérifie ou non le critère.
Pour rendre ce résultat un peu plus lisible nous nous définissons une petite fonction de confort.
w2-s2-c4-expressions-regulieres 21
MOOC Python 3 Semaine 2, Séquence 2
REGEXP=[0-9]+[A-Za-z]+[0-9]+[A-Za-z]+[0-9]+
890hj000nnm890 → Match!
123abc456def789 → Match!
8090abababab879 → no
Ici plutôt que d’utiliser les raccourcis comme \w j’ai préféré écrire explicitement les ensembles de carac-
tères en jeu. De cette façon, on rend son code indépendant du LOCALE si c’est ce qu’on veut faire. Il y
a deux morceaux qui interviennent tour à tour :
Et comme tout à l’heure on a simplement juxtaposé les morceaux dans le bon ordre pour construire
l’expression régulière complète.
[22]: '123abc456def789'
Maintenant, on va même pouvoir donner un nom à un morceau de la regexp, ici on désigne par needle
le groupe de chiffres du milieu.
Et une fois que c’est fait, on peut demander à l’outil de nous retrouver la partie correspondante dans la
chaine initiale :
456
w2-s2-c4-expressions-regulieres 22
MOOC Python 3 Semaine 2, Séquence 2
— ?P<needle> spécifie que ce groupe pourra être référencé sous le nom needle (cette syntaxe très
absconse est héritée semble-t-il de perl).
Un troisième exemple
Enfin, et c’est un trait qui n’est pas présent dans tous les langages, on peut restreindre un morceau de
chaîne à être identique à un groupe déjà vu plus tôt dans la chaîne. Dans l’exemple ci-dessus, on pourrait
ajouter comme contrainte que le premier et le dernier groupes de chiffres soient identiques, comme ceci
Si bien que maintenant, avec les mêmes entrées que tout à l’heure
[26]: print(f"REGEXP={regexp3}\n")
for sample in samples:
match = [Link](regexp3, sample)
print(f"{sample:>16} → {nice(match)}")
REGEXP=(?P<id>[0-9]+)[A-Za-z]+(?P<needle>[0-9]+)[A-Za-z]+(?P=id)
890hj000nnm890 → Match!
123abc456def789 → no
8090abababab879 → no
Comme précédemment on a défini le groupe nommé id comme étant la première suite de chiffres. La
nouveauté ici est la contrainte qu’on a imposée sur le dernier groupe avec (?P=id). Comme vous le voyez,
on n’obtient un match qu’avec les entrées dans lesquelles le dernier groupe de chiffres est identique au
premier.
Avant d’apprendre à écrire une expression régulière, disons quelques mots du mode d’emploi de la librairie.
Comme vous le savez peut-être, une expression régulière décrite sous forme de chaîne, comme par exemple
"\w*[am]\W", peut être traduite dans un automate fini qui permet de faire le filtrage avec une chaîne.
C’est ce qui explique le workflow que nous avons résumé dans cette figure.
La méthode recommandée pour utiliser la librairie, lorsque vous avez le même pattern à appliquer à un
grand nombre de chaînes, est de :
— compiler une seule fois votre chaîne en un automate, qui est matérialisé par un objet de la classe
[Link], en utilisant [Link],
— puis d’utiliser directement cet objet autant de fois que vous avez de chaînes.
Nous avons utilisé dans les exemples plus haut (et nous continuerons plus bas pour une meilleure lisibilité)
des fonctions de commodité du module, qui sont pratiques, par exemple, pour mettre au point une
expression régulière en mode interactif, mais qui ne sont pas forcément adaptées dans tous les cas.
w2-s2-c4-expressions-regulieres 23
MOOC Python 3 Semaine 2, Séquence 2
Donc à chaque fois qu’on utilise une fonction de commodité, on recompile la chaîne en automate, ce qui,
dès qu’on a plus d’une chaîne à traiter, représente un surcoût.
890hj000nnm890 → Match!
123abc456def789 → no
8090abababab879 → no
890hj000nnm890 → Match!
123abc456def789 → no
8090abababab879 → no
Cette deuxième version ne compile qu’une fois la chaîne en automate, et donc est plus efficace.
Les objets de la classe RegexObject représentent donc l’automate à état fini qui est le résultat de la
compilation de l’expression régulière. Pour résumer ce qu’on a déjà vu, les méthodes les plus utiles sur
un objet RegexObject sont :
— match et search, qui cherchent un match soit uniquement au début (match) ou n’importe où dans
la chaîne (search),
— findall et split pour chercher toutes les occurrences (findall) ou leur négatif (split),
— sub (qui aurait pu sans doute s’appeler replace, mais c’est comme ça) pour remplacer les occur-
rences de pattern.
Exploiter le résultat
Les méthodes disponibles sur la classe [Link] sont documentées en détail ici. On en a déjà
rencontré quelques-unes, en voici à nouveau un aperçu rapide.
[29]: # exemple
sample = " Isaac Newton, physicist"
match = [Link](r"(\w+) (?P<name>\w+)", sample)
[30]: [Link]
w2-s2-c4-expressions-regulieres 24
MOOC Python 3 Semaine 2, Séquence 2
[31]: [Link]
group, groups, groupdict pour retrouver les morceaux de la chaîne d’entrée qui correspondent aux
groupes de la regexp. On peut y accéder par rang, ou par nom (comme on l’a vu plus haut avec needle).
[32]: [Link]()
[33]: [Link](1)
[33]: 'Isaac'
[34]: [Link]('name')
[34]: 'Newton'
[35]: [Link](2)
[35]: 'Newton'
[36]: [Link]()
Comme on le voit pour l’accès par rang les indices commencent à 1 pour des raisons historiques (on
pouvait déjà référencer \1 dans l’éditeur Unix sed à la fin des années 70 !).
On peut aussi accéder au groupe 0 comme étant la partie de la chaîne de départ qui a effectivement été
filtrée par l’expression régulière - qui en général est une sous-chaine de la chaîne de départ :
expand permet de faire une espèce de [Link] avec les valeurs des groupes.
w2-s2-c4-expressions-regulieres 25
MOOC Python 3 Semaine 2, Séquence 2
span pour connaître les index dans la chaîne d’entrée pour un groupe donné.
[40]: # NB: seq[i:j] est une opération de slicing que nous verrons plus tard
# Elle retourne une séquence contenant les éléments de i à j-1 de seq
begin, end = [Link]('name')
sample[begin:end]
[40]: 'Newton'
Enfin il faut noter qu’on peut passer à [Link] un certain nombre de flags qui modifient globalement
l’interprétation de la chaîne, et qui peuvent rendre service.
Vous trouverez une liste exhaustive de ces flags ici. Ils ont en général un nom long et parlant, et un alias
court sur un seul caractère. Les plus utiles sont sans doute :
— IGNORECASE (alias I) pour, eh bien, ne pas faire la différence entre minuscules et majuscules,
— UNICODE (alias U) pour rendre les séquences \w et autres basées sur les propriétés des caractères
dans la norme Unicode,
— LOCALE (alias L) cette fois \w dépend du locale courant,
— MULTILINE (alias M), et
— DOTALL (alias S) - pour ces deux derniers flags, voir la discussion à la fin du complément.
Comme c’est souvent le cas, on doit passer à [Link] un ou logique (caractère |) des différents flags
que l’on veut utiliser, c’est-à-dire qu’on fera par exemple
MAX_REPEAT 0 MAXREPEAT
LITERAL 97
MAX_REPEAT 1 MAXREPEAT
LITERAL 98
Nous pouvons à présent voir comment construire une expression régulière, en essayant de rester synthé-
tique (la documentation du module re en donne une version exhaustive).
w2-s2-c4-expressions-regulieres 26
MOOC Python 3 Semaine 2, Séquence 2
— un seul caractère :
— vous le citez tel quel, en le précédent d’un backslash \ s’il a par ailleurs un sens spécial dans
le micro-langage de regexps (comme +, *, [, etc.) ;
— l’attrape-tout (wildcard) :
— un point . signifie “n’importe quel caractère” ;
— un ensemble de caractères avec la notation [...] qui permet de décrire par exemple :
— [a1=] un ensemble in extenso, ici un caractère parmi a, 1, ou =,
— [a-z] un intervalle de caractères, ici de a à z,
— [15e-g] un mélange des deux, ici un ensemble qui contiendrait 1, 5, e, f et g,
— [^15e-g] une négation, qui a ^ comme premier caractère dans les [], ici tout sauf l’ensemble
précédent ;
— un ensemble prédéfini de caractères, qui peuvent alors dépendre de l’environnement (UNICODE et
LOCALE) avec entre autres les notations :
— \w les caractères alphanumériques, et \W (les autres),
— \s les caractères “blancs” - espace, tabulation, saut de ligne, etc., et \S (les autres),
— \d pour les chiffres, et \D (les autres).
Pour ce dernier exemple, comme on a backslashé le . il faut que la chaîne en entrée contienne vraiment
un .
Match!
En série ou en parallèle
Si je fais une analogie avec les montages électriques, jusqu’ici on a vu le montage en série, on met des
expressions régulières bout à bout qui filtrent (match) la chaine en entrée séquentiellement du début à
la fin. On a un peu de marge pour spécifier des alternatives, lorsqu’on fait par exemple
"ab[cd]ef"
mais c’est limité à un seul caractère. Si on veut reconnaitre deux mots qui n’ont pas grand-chose à voir
comme abc ou def, il faut en quelque sorte mettre deux regexps en parallèle, et c’est ce que permet
l’opérateur |
w2-s2-c4-expressions-regulieres 27
MOOC Python 3 Semaine 2, Séquence 2
Fin(s) de chaîne
Selon que vous utilisez match ou search, vous précisez si vous vous intéressez uniquement à un match
en début (match) ou n’importe où (search) dans la chaîne.
Mais indépendamment de cela, il peut être intéressant de “coller” l’expression en début ou en fin de ligne,
et pour ça il existe des caractères spéciaux :
— ^ lorsqu’il est utilisé comme un caractère (c’est à dire pas en début de []) signifie un début de
chaîne ;
— \A a le même sens (sauf en mode MULTILINE), et je le recommande de préférence à ^ qui est déjà
pas mal surchargé ;
— $ matche une fin de ligne ;
— \Z est voisin de $ mais pas tout à fait identique.
Reportez-vous à la documentation pour le détails des différences. Attention aussi à entrer le ^ correcte-
ment, il vous faut le caractère ASCII et non un voisin dans la ménagerie Unicode.
On a en effet bien le pattern bc dans la chaine en entrée, mais il n’est ni au début ni à la fin.
Parenthéser - (grouper)
w2-s2-c4-expressions-regulieres 28
MOOC Python 3 Semaine 2, Séquence 2
abcf → Match!
adef → Match!
abef → no
abf → no
Les parenthèses jouent un rôle additionel de groupe, ce qui signifie qu’on peut retrouver le texte corres-
pondant à l’expression régulière comprise dans les (). Par exemple, pour le premier match
dans cet exemple, on n’a utilisé qu’un seul groupe (), et le morceau de chaîne qui correspond à ce groupe
se trouve donc être le seul groupe retourné par [Link].
— * l’étoile qui signifie n’importe quel nombre, même nul, d’occurrences - par exemple, (ab)* pour
indiquer '' ou 'ab' ou 'abab' ou etc.,
— + le plus qui signifie au moins une occurrence - e.g. (ab)+ pour ab ou abab ou ababab ou etc,
— ? qui indique une option, c’est-à-dire 0 ou 1 occurence - autrement dit (ab)? matche '' ou ab,
— {n} pour exactement n occurrences de (ab) - e.g. (ab){3} qui serait exactement équivalent à
ababab,
— {m,n} entre m et n fois inclusivement.
w2-s2-c4-expressions-regulieres 29
MOOC Python 3 Semaine 2, Séquence 2
/ \A(ab)*\Z → Match!
ab / \A(ab)*\Z → Match!
ababab / \A(ab)*\Z → Match!
abababab / \A(ab)*\Z → Match!
baba / \A(ab)*\Z → no
/ \A(ab)+\Z → no
ab / \A(ab)+\Z → Match!
ababab / \A(ab)+\Z → Match!
abababab / \A(ab)+\Z → Match!
baba / \A(ab)+\Z → no
/ \A(ab){3}\Z → no
ab / \A(ab){3}\Z → no
ababab / \A(ab){3}\Z → Match!
abababab / \A(ab){3}\Z → no
baba / \A(ab){3}\Z → no
/ \A(ab){3,4}\Z → no
ab / \A(ab){3,4}\Z → no
ababab / \A(ab){3,4}\Z → Match!
abababab / \A(ab){3,4}\Z → Match!
baba / \A(ab){3,4}\Z → no
Groupes et contraintes
Nous avons déjà vu un exemple de groupe nommé (voir needle plus haut), les opérateurs que l’on peut
citer dans cette catégorie sont :
Greedy vs non-greedy
Lorsqu’on stipule une répétition un nombre indéfini de fois, il se peut qu’il existe plusieurs façons de
filtrer l’entrée avec l’expression régulière. Que ce soit avec *, ou +, ou ?, l’algorithme va toujours essayer
de trouver la séquence la plus longue, c’est pourquoi on qualifie l’approche de greedy - quelque chose
comme glouton en français.
# on obtient ceci
# on rappelle que group(0) montre la partie du fragment
# HTML qui matche l'expression régulière
match = [Link](re_greedy, line)
w2-s2-c4-expressions-regulieres 30
MOOC Python 3 Semaine 2, Séquence 2
[Link](0)
[51]: '<h1>Title</h1>'
Ça n’est pas forcément ce qu’on voulait faire, aussi on peut spécifier l’approche inverse, c’est-à-dire de
trouver la plus-petite chaîne qui matche, dans une approche dite non-greedy, avec les opérateurs suivants :
— *? : * mais non-greedy,
— +? : + mais non-greedy,
— ?? : ? mais non-greedy,
[52]: '<h1>'
Il peut être utile, pour conclure cette présentation, de préciser un peu le comportement de la librairie
vis-à-vis des fins de ligne.
Historiquement, les expressions régulières telles qu’on les trouve dans les librairies C, donc dans sed,
grep et autre utilitaires Unix, sont associées au modèle mental où on filtre les entrées ligne par ligne.
Vous voyez donc que l’attrape-tout '.' en fait n’attrape pas le caractère de fin de ligne \n, puisque si
c’était le cas et compte tenu du coté greedy de l’algorithme on devrait voir ici tout le contenu de sample.
Il existe un flag [Link] qui permet de faire de . un vrai attrape-tout qui capture aussi les newline
w2-s2-c4-expressions-regulieres 31
MOOC Python 3 Semaine 2, Séquence 2
Cela dit, le caractère newline est par ailleurs considéré comme un caractère comme un autre, on peut le
mentionner dans une regexp comme les autres. Voici quelques exemples pour illustrer tout ceci
Conclusion
La mise au point d’expressions régulières est certes un peu exigeante, et demande pas mal de pratique,
mais permet d’écrire en quelques lignes des fonctionnalités très puissantes, c’est un investissement très
rentable :)
Je vous signale enfin l’existence de sites web qui évaluent une expression régulière de manière interactive
et qui peuvent rendre la mise au point moins fastidieuse.
Un élève, qui a eu notamment des soucis avec le \w sur [Link] (dont, on l’a vu, la signification dépend
du locale de la machine hôte) recommande pour sa part [Link] : > Ce site est très
didactique et lui reconnait les caractères accentués sur un \w sans rajouter de flag (même si cette option
est possible).
Pour ceux qui ont quelques rudiments de la théorie des langages, vous savez qu’on distingue en général
— l’analyse lexicale, qui découpe le texte en morceaux (qu’on appelle des tokens),
— et l’analyse syntaxique qui décrit pour simplifier à l’extrême l’ordre dans lequel on peut trouver les
tokens.
Avec les expression régulières, on adresse le niveau de l’analyse lexicale. Pour l’analyse syntaxique, qui
est franchement au delà des objectifs de ce cours, il existe de nombreuses alternatives, parmi lesquelles :
— pyparsing
— PLY (Python Lex-Yacc)
w2-s2-c4-expressions-regulieres 32
MOOC Python 3 Semaine 2, Séquence 2
— ANTLR qui est un outil écrit en Java mais qui peut générer des parsers en python,
— …
2.6 w2-s2-x1-expressions-regulieres
Expressions régulières
Nous vous proposons dans ce notebook quelques exercices sur les expressions régulières. Faisons quelques
remarques avant de commencer :
— nous nous concentrons sur l’écriture de l’expression régulière en elle-même, et pas sur l’utilisation
de la bibliothèque ;
— en particulier, tous les exercices font appel à [Link] entre votre regexp et une liste de chaînes
d’entrée qui servent de jeux de test.
Liens utiles
Pour travailler sur ces exercices, il pourra être profitable d’avoir sous la main :
— la documentation officielle ;
— et [Link] (par exemple) qui permet de mettre au point de manière interactive,
et donc d’avoir un retour presque immédiat, pour accélérer la mise au point.
Identificateurs Python
On vous demande d’écrire une expression régulière qui décrit les noms de variable en Python. Pour cet
exercice on se concentre sur les caractères ASCII. On exclut donc les noms de variables qui pourraient
contenir des caractères exotiques comme les caractères accentués ou autres lettres grecques.
Il s’agit donc de reconnaître toutes les chaînes qui commencent par une lettre ou un _, suivi de lettres,
chiffres ou _.
regexp_pythonid = r"votre_regexp"
# NOTE
w2-s2-x1-expressions-regulieres 33
MOOC Python 3 Semaine 2, Séquence 2
On veut reconnaître dans un fichier toutes les lignes qui contiennent un nom et un prénom.
[5]: exo_agenda.example()
— commencent par une suite - possiblement vide - de caractères alphanumériques (vous pouvez utiliser
\w) ou tiret haut (-) qui constitue le prénom ;
— contiennent ensuite comme séparateur le caractère ‘deux-points’ : ;
— contiennent ensuite une suite - cette fois jamais vide - de caractères alphanumériques, qui constitue
le nom ;
— et enfin contiennent un deuxième caractère : mais optionnellement seulement.
On vous demande de construire une expression régulière qui définit les deux groupes nom et prenom, et
qui rejette les lignes qui ne satisfont pas ces critères.
Dans la correction - et ce sera pareil pour tous les exercices de regexp où on demande des groupes - la
correction affiche uniquement les groupes demandés ; ici on va vous montrer les groupes nom et prenom ;
vous avez parfaitement le droit d’utiliser des groupes supplémentaires, nommés ou pas d’ailleurs, dans
votre propre regexp.
regexp_agenda = r"(?P<prenom>)(?P<prenom>)\Z"
# NOTE
# auto-exec-for-latex has skipped execution of this cell
w2-s2-x1-expressions-regulieres 34
MOOC Python 3 Semaine 2, Séquence 2
Numéros de téléphone
Cette fois on veut reconnaître des numéros de téléphone français, qui peuvent être :
Dans tous les cas on veut trouver dans le groupe number les 9 chiffres vraiment significatifs, comme ceci :
[8]: exo_phone.example()
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Vu comment sont conçus les exercices, vous ne pouvez pas passer à [Link] un drapeau comme
[Link] ou autre ; sachez cependant que vous pouvez embarquer ces drapeaux dans la regexp
elle-même ; par exemple pour rendre la regexp insensible à la casse de caractères, au lieu d’appeler
[Link] avec le flag re.I, vous pouvez utiliser (?i) comme ceci :
[10]: import re
[11]: 'ABC'
[12]: # ou cela
[Link]("(?i)abc","ABC").group(0)
w2-s2-x1-expressions-regulieres 35
MOOC Python 3 Semaine 2, Séquence 2
[12]: 'ABC'
/Users/tparment/miniconda3/envs/flotpython-course/lib/python3.7/site-packag�
�es/ipykernel_launcher.py:3: DeprecationWarning: Flags not at the start o�
�f the expression 'abc(?i)'
This is separate from the ipykernel package so we can avoid doing imports�
� until
[13]: 'ABC'
Pour plus de précisions sur ce trait, que nous avons laissé de côté dans le complément pour ne pas trop
l’alourdir, voyez la documentation sur les expressions régulières et cherchez la première occurrence de
iLmsux.
On vous demande d’écrire une expression régulière qui permette d’analyser des URLs.
Enfin, vous devez définir les groupes proto, user, password, hostname, port et path qui sont utilisés
pour vérifier votre résultat. Dans la case Résultat attendu, vous trouverez soit None si la regexp ne filtre
pas l’intégralité de l’entrée, ou bien une liste ordonnée de tuples qui donnent la valeur de ces groupes ;
vous n’avez rien à faire pour construire ces tuples, c’est l’exercice qui s’en occupe.
w2-s2-x1-expressions-regulieres 36
MOOC Python 3 Semaine 2, Séquence 3
regexp_url = "<votre_regexp>"
[ ]: exo_url.correction(regexp_url)
# NOTE
# auto-exec-for-latex has skipped execution of this cell
2.7 w2-s3-c1-slices
Nous allons illustrer les slices sur la chaîne suivante, rappelez-vous toutefois que ce mécanisme fonctionne
avec toutes les séquences que l’on verra plus tard, comme les listes ou les tuples.
abcdefghijklmnopqrstuvwxyz
On a vu en cours qu’une slice permet de désigner toute une plage d’éléments d’une séquence. Ainsi on
peut écrire :
[2]: chaine[2:6]
[2]: 'cdef'
Les débutants ont parfois du mal avec les bornes. Il faut se souvenir que :
w2-s3-c1-slices 37
MOOC Python 3 Semaine 2, Séquence 3
Pour vous aider à vous souvenir des conventions de début et de fin, souvenez-vous qu’on veut pouvoir
facilement juxtaposer deux slices qui ont une borne commune.
C’est-à-dire qu’avec :
[3]: True
[4]: 'abcdef'
[5]: 'yz'
[6]: 'abcdefghijklmnopqrstuvwxyz'
Indices négatifs On peut utiliser des indices négatifs pour compter à partir de la fin :
w2-s3-c1-slices 38
MOOC Python 3 Semaine 2, Séquence 3
[7]: chaine[3:-3]
[7]: 'defghijklmnopqrstuvw'
[8]: chaine[-3:]
[8]: 'xyz'
Il est également possible de préciser un pas, de façon à ne choisir par exemple, dans la plage donnée,
qu’un élément sur deux :
[9]: 'dfhjlnprtv'
Comme on le devine, le troisième élément de la slice, ici 2, détermine le pas. On ne retient donc, dans la
chaîne defghi... que d, puis f, et ainsi de suite.
On peut préciser du coup la borne de fin (ici -3) avec un peu de liberté, puisqu’ici on obtiendrait un
résultat identique avec -4.
[10]: chaine[3:-4:2]
[10]: 'dfhjlnprtv'
Pas négatif
Il est même possible de spécifier un pas négatif. Dans ce cas, de manière un peu contre-intuitive, il faut
préciser un début (le premier indice de la slice) qui soit plus à droite que la fin (le second indice).
Pour prendre un exemple, comme l’élément d’indice -3, c’est-à-dire x, est plus à droite que l’élément
d’indice 3, c’est-à-dire d, évidemment si on ne précisait pas le pas (qui revient à choisir un pas égal à 1),
on obtiendrait une liste vide :
[11]: chaine[-3:3]
[11]: ''
[12]: chaine[-3:3:-2]
[12]: 'xvtrpnljhf'
w2-s3-c1-slices 39
MOOC Python 3 Semaine 2, Séquence 3
Conclusion
À nouveau, souvenez-vous que tous ces mécanismes fonctionnent avec de nombreux autres types que les
chaînes de caractères. En voici deux exemples qui anticipent tous les deux sur la suite, mais qui devraient
illustrer les vastes possibilités qui sont offertes avec les slices.
[14]: liste[-1:1:-2]
[14]: [32, 8]
Voici une représentation imagée de ce qui se passe lorsqu’on exécute cette dernière ligne de code ; cela
revient en quelque sorte à remplacer la slice à gauche de l’affectation (ici liste[2:4]) par la liste à
droite de l’affectation (ici [10, 20, 30] - ce qui a, en général, pour effet de modifier la longueur de la
liste.
numpy La bibliothèque numpy permet de manipuler des tableaux ou des matrices. En anticipant (beau-
coup) sur son usage que nous reverrons bien entendu en détail, voici un aperçu de ce que l’on peut faire
avec des slices sur des objets numpy :
w2-s3-c1-slices 40
MOOC Python 3 Semaine 2, Séquence 4
import numpy as np
Sur ce tableau de taille 5x5, nous pouvons aussi faire du slicing et extraire le sous-tableau 3x3 au centre :
w2-s4-c1-listes 41
MOOC Python 3 Semaine 2, Séquence 4
2.8 w2-s4-c1-listes
Trouver l’information
Pour commencer, rappelons comment retrouver la liste des méthodes définies sur le type list :
[ ]: help(list)
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Ignorez les méthodes dont le nom commence et termine par __ (nous parlerons de ceci en semaine 6),
vous trouvez alors les méthodes utiles listées entre append et sort.
Certaines de ces méthodes ont été vues dans la vidéo sur les séquences, c’est le cas notamment de count
et index.
Nous allons à présent décrire les autres, partiellement et brièvement. Un autre complément décrit la mé-
thode sort. Reportez-vous au lien donné en fin de notebook pour obtenir une information plus complète.
liste [0, 1, 2, 3]
Avertissements :
— soyez bien attentifs au nombre de fois où vous exécutez les cellules de ce notebook ;
— par exemple une liste renversée deux fois peut donner l’impression que reverse ne marche pas ;
— n’hésitez pas à utiliser le menu Cell -> Run All pour réexécuter en une seule fois le notebook entier.
append
[2]: [Link]('ap')
print('liste', liste)
extend
La méthode extend réalise la même opération, mais avec tous les éléments de la liste qu’on lui passe en
argument :
w2-s4-c1-listes 42
MOOC Python 3 Semaine 2, Séquence 4
append vs +
Ces deux méthodes append et extend sont donc assez voisines ; avant de voir d’autres méthodes de list,
prenons un peu le temps de comparer leur comportement avec l’addition + de liste. L’élément clé ici, on
l’a déjà vu dans la vidéo, est que la liste est un objet mutable. append et extend modifient la liste sur
laquelle elles travaillent, alors que l’addition crée un nouvel objet.
[4]: # pour créer une liste avec les n premiers entiers, on utilise
# la fonction built-in range(), que l'on convertit en liste
# on aura l'occasion d'y revenir
a1 = list(range(3))
print(a1)
[0, 1, 2]
a1 [0, 1, 2]
a2 [10, 11, 12]
a3 [0, 1, 2, 10, 11, 12]
Comme on le voit, après une addition, les deux termes de l’addition sont inchangés. Pour bien comprendre,
voyons exactement le même scénario sous pythontutor :
Note : une fois que vous avez évalué la cellule avec %%ipythontutor, vous devez cliquer sur le bouton
Forward pour voir pas à pas le comportement du programme.
# NOTE
# auto-exec-for-latex has skipped execution of this cell
w2-s4-c1-listes 43
MOOC Python 3 Semaine 2, Séquence 4
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Ici on tire profit du fait que la liste est un objet mutable : extend modifie l’objet sur lequel on l’appelle
(ici e1). Dans ce scénario on ne crée en tout que deux objets, et du coup il est inutile pour extend de
renvoyer quoi que ce soit, et c’est pourquoi e3 ici vaut None.
— l’addition est disponible sur tous les types séquences - on peut toujours réaliser l’addition puisqu’on
crée un nouvel objet pour stocker le résultat de l’addition ;
— mais append et extend ne sont par exemple pas disponibles sur les chaînes de caractères, qui sont
immuables - si e1 était une chaîne, on ne pourrait pas la modifier pour lui ajouter des éléments.
insert
Reprenons notre inventaire des méthodes de list, et pour cela rappelons nous le contenu de la variable
liste :
[9]: liste
La méthode insert permet, comme le nom le suggère, d’insérer un élément à une certaine position ;
comme toujours les indices commencent à zéro et donc :
On peut remarquer qu’un résultat analogue peut être obtenu avec une affectation de slice ; par exemple
pour insérer au rang 5 (i.e. avant ap), on pourrait aussi bien faire :
remove
[12]: [Link](3)
print('liste', liste)
w2-s4-c1-listes 44
MOOC Python 3 Semaine 2, Séquence 4
pop
La méthode pop prend en argument un indice ; elle permet d’extraire l’élément à cet indice. En un seul
appel on obtient la valeur de l’élément et on l’enlève de la liste :
popped 0 liste [1, '1 bis', 2, '3 bis', 'ap', 'ex1', 'ex2']
Si l’indice n’est pas précisé, c’est le dernier élément de la liste qui est visé :
popped ex2 liste [1, '1 bis', 2, '3 bis', 'ap', 'ex1']
reverse
[15]: [Link]()
print('liste', liste)
On peut remarquer ici que le résultat se rapproche de ce qu’on peut obtenir avec une opération de slicing
comme ceci :
À la différence toutefois qu’avec le slicing c’est une copie de la liste initiale qui est retournée, la liste de
départ quant à elle n’est pas modifiée.
[Link]
help avec ? Je vous signale en passant que dans un notebook vous pouvez obtenir de l’aide avec un
point d’interrogation ? inséré avant ou après un symbole. Par exemple pour obtenir des précisions sur la
méthode [Link], on peut faire soit :
w2-s4-c1-listes 45
MOOC Python 3 Semaine 2, Séquence 4
Help on method_descriptor:
pop(self, index=-1, /)
Remove and return item at index (default last).
Complétion avec Tab Dans un notebook vous avez aussi la complétion ; si vous tapez, dans une cellule
de code, le début d’un mot connu dans l’environnement, vous voyez apparaître un dialogue avec les noms
connus qui commencent par ce mot ici li ; utilisez les flèches pour choisir, et ‘Return’ pour sélectionner.
# NOTE
# auto-exec-for-latex has skipped execution of this cell
2.9 w2-s4-c2-listes-mutables
Voici un exemple d’un fragment de code qui illustre le caractère immuable des chaînes de caractères.
Nous l’exécutons sous pythontutor, afin de bien illustrer les relations entre variables et objets.
Note : une fois que vous avez évalué la cellule avec %%ipythontutor, vous devez cliquer sur le bouton
Forward pour voir pas à pas le comportement du programme.
Le scénario est très simple, on crée deux variables s1 et s2 vers le même objet 'abc', puis on fait une
opération += sur la variable s1.
Comme l’objet est une chaîne, il est donc immuable, on ne peut pas modifier l’objet directement ; pour
obtenir l’effet recherché (à savoir que s1 s’allonge de 'def'), Python crée un deuxième objet, comme on
le voit bien sous pythontutor :
[ ]: %%ipythontutor heapPrimitives=true
# deux variables vers le même objet
s1 = 'abc'
w2-s4-c2-listes-mutables 46
MOOC Python 3 Semaine 2, Séquence 4
s2 = s1
# on essaie de modifier l'objet
s1 += 'def'
# pensez à cliquer sur `Forward`
# NOTE
# auto-exec-for-latex has skipped execution of this cell
abcdef
abc
Voici ce qu’on obtient par contraste pour le même scénario mais qui cette fois utilise des listes, qui sont
des objets mutables :
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Conclusion
Ce comportement n’est pas propre à l’usage de l’opérateur +=, les objets mutables et immuables ont par
essence un comportement différent, il est très important d’avoir ceci présent à l’esprit.
Nous aurons notamment l’occasion d’approfondir cela dans la séquence consacrée aux références parta-
gées, en semaine 3.
w2-s4-c3-tris-de-liste-1 47
MOOC Python 3 Semaine 2, Séquence 4
2.10 w2-s4-c3-tris-de-liste-1
Tris de listes
Python fournit une méthode standard pour trier une liste, qui s’appelle, sans grande surprise, sort.
La méthode sort
On retrouve ici, avec l’instruction [Link]() un cas d’appel de méthode (ici sort) sur un objet (ici
liste), comme on l’avait vu dans la vidéo.
La première chose à remarquer est que la liste d’entrée a été modifiée, on dit “en place”, ou encore “par
effet de bord”. Voyons cela sous pythontutor :
# NOTE
# auto-exec-for-latex has skipped execution of this cell
On aurait pu imaginer que la liste d’entrée soit restée inchangée, et que la méthode de tri renvoie une
copie triée de la liste, ce n’est pas le choix qui a été fait, cela permet d’économiser des allocations mémoire
autant que possible et d’accélérer sensiblement le tri.
La fonction sorted
Si vous avez besoin de faire le tri sur une copie de votre liste, la fonction sorted vous permet de le faire :
# NOTE
# auto-exec-for-latex has skipped execution of this cell
w2-s4-c3-tris-de-liste-1 48
MOOC Python 3 Semaine 2, Séquence 4
Tri décroissant
Revenons à la méthode sort et aux tris en place. Par défaut la liste est triée par ordre croissant, si au
contraire vous voulez l’ordre décroissant, faites comme ceci :
Nous n’avons pas encore vu à quoi correspond cette formule reverse=True dans l’appel à la méthode
- ceci sera approfondi dans le chapitre sur les appels de fonction - mais dans l’immédiat vous pouvez
utiliser cette technique telle quelle.
Chaînes de caractères
Cette technique fonctionne très bien sur tous les types numériques (enfin, à l’exception des complexes ;
en guise d’exercice, pourquoi ?), ainsi que sur les chaînes de caractères :
Comme on s’y attend, il s’agit cette fois d’un tri lexicographique, dérivé de l’ordre sur les caractères.
Autrement dit, c’est l’ordre du dictionnaire. Il faut souligner toutefois, pour les personnes n’ayant jamais
été exposées à l’informatique, que cet ordre, quoique déterministe, est arbitraire en dehors des lettres de
l’alphabet.
[5]: True
[6]: True
w2-s4-c3-tris-de-liste-1 49
MOOC Python 3 Semaine 2, Séquence 5
print(liste)
['Zoo', 'abc']
Et lorsque les chaînes contiennent des espaces ou autres ponctuations, le résultat du tri peut paraître
surprenant :
[8]: # attention ici notre premiere chaîne commence par une espace
# et le caractère 'Espace' est plus petit
# que tous les autres caractères imprimables
liste = [' zoo', 'ane']
[Link]()
print(liste)
À suivre
Il est possible de définir soi-même le critère à utiliser pour trier une liste, et nous verrons cela bientôt,
une fois que nous aurons introduit la notion de fonction.
2.11 w2-s5-c1-indentations
Indentations en Python
Imbrications
Nous l’avons vu dans la vidéo, la pratique la plus courante est d’utiliser systématiquement une indentation
de 4 espaces :
OUI
w2-s5-c1-indentations 50
MOOC Python 3 Semaine 2, Séquence 5
a mais pas b
Dans cette construction assez simple, remarquez bien les deux points ‘ :’ à chaque début de bloc, c’est-
à-dire à chaque fin de ligne if ou else.
Cette façon d’organiser le code peut paraître très étrange, notamment aux gens habitués à un autre
langage de programmation, puisqu’en général les syntaxes des langages sont conçues de manière à être
insensibles aux espaces et à la présentation.
Comme vous le constaterez à l’usage cependant, une fois qu’on s’y est habitué cette pratique est très
agréable, une fois qu’on a écrit la dernière ligne du code, on n’a pas à réfléchir à refermer le bon nombre
d’accolades ou de end.
Par ailleurs, comme pour tous les langages, votre éditeur favori connaît cette syntaxe et va vous aider à
respecter la règle des 4 caractères. Nous ne pouvons pas publier ici une liste des commandes disponibles
par éditeur, nous vous invitons le cas échéant à échanger entre vous sur le forum pour partager les recettes
que vous utilisez avec votre éditeur / environnement de programmation favori.
Espaces vs tabulations
Version courte Il nous faut par contre donner quelques détails sur un problème que l’on rencontre
fréquemment sur du code partagé entre plusieurs personnes quand celles-ci utilisent des environnement
différents.
Pour faire court, ce problème est susceptible d’apparaître dès qu’on utilise des tabulations, plutôt que
des espaces, pour implémenter les indentations. Aussi, le message à retenir ici est de ne jamais utiliser
de tabulations dans votre code Python. Tout bon éditeur Python devrait faire cela par défaut.
Version longue En version longue, il existe un code ASCII pour un caractère qui s’appelle Tabulation
(alias Control-i, qu’on note aussi ^I) ; l’interprétation de ce caractère n’étant pas clairement spécifiée, il
arrive qu’on se retrouve dans une situation comme la suivante.
Bernard utilise l’éditeur vim ; sous cet éditeur il lui est possible de mettre des tabulations dans son code,
et de choisir la valeur de ces tabulations. Aussi il va dans les préférences de vim, choisit Tabulation=4,
et écrit un programme qu’il voit comme ceci :
w2-s5-c1-indentations 51
MOOC Python 3 Semaine 2, Séquence 5
a mais pas b
Sauf qu’en fait, il a mis un mélange de tabulations et d’espaces, et en fait le fichier contient (avec ^I
pour tabulation) :
if 'a' in entree:
^Iif 'b' in entree:
^I^Icas11 = True
^Iprint('a et b')
^Ielse:
^I^Icas12 = True
^Iprint('a mais pas b')
Remarquez le mélange de Tabulations et d’espaces dans les deux lignes avec print. Bernard envoie son
code à Alice qui utilise emacs. Dans son environnement, emacs affiche une tabulation comme 8 caractères.
Du coup Alice “voit” le code suivant :
if 'a' in entree:
if 'b' in entree:
cas11 = True
print('a et b')
else:
cas12 = True
print('a mais pas b')
Bref, c’est la confusion la plus totale. Aussi répétons-le, n’utilisez jamais de tabulations dans votre code
Python.
Ce qui ne veut pas dire qu’il ne faut pas utiliser la touche Tab avec votre éditeur - au contraire, c’est une
touche très utilisée - mais faites bien la différence entre le fait d’appuyer sur la touche Tab et le fait que le
fichier sauvé sur disque contient effectivement un caractère tabulation. Votre éditeur favori propose très
certainement une option permettant de faire les remplacements idoines pour ne pas écrire de tabulation
dans vos fichiers, tout en vous permettant d’indenter votre code avec la touche Tab.
Signalons enfin que Python 3 est plus restrictif que Python 2 à cet égard, et interdit de mélanger des
espaces et des tabulations sur une même ligne. Ce qui n’enlève rien à notre recommandation.
Vous pouvez trouver du code qui ne respecte pas la convention des 4 caractères.
Version longue En version longue, et pour les curieux : Python n’impose pas que les indentations soient
de 4 caractères. Aussi vous pouvez rencontrer un code qui ne respecte pas cette convention, et il nous
faut, pour être tout à fait précis sur ce que Python accepte ou non, préciser ce qui est réellement requis
par Python.
La règle utilisée pour analyser votre code, c’est que toutes les instructions dans un même bloc soient
présentées avec le même niveau d’indentation. Si deux lignes successives - modulo les blocs imbriqués -
ont la même indentation, elles sont dans le même bloc.
w2-s5-c1-indentations 52
MOOC Python 3 Semaine 2, Séquence 5
Voyons quelques exemples. Tout d’abord le code suivant est légal, quoique, redisons-le pour la dernière
fois, pas du tout recommandé :
OUI
En effet, les deux blocs (après if et après else) sont des blocs distincts, ils sont libres d’utiliser deux
indentations différentes (ici 2 et 6).
# NOTE
# auto-exec-for-latex has skipped execution of this cell
En effet les deux lignes if et else font logiquement partie du même bloc, elles doivent donc avoir la
même indentation. Avec cette présentation le lecteur Python émet une erreur et ne peut pas interpréter
le code.
2.12 w2-s5-c2-presentation
La PEP-008
On trouve dans la PEP-008 (en anglais) les conventions de codage qui s’appliquent à toute la librai-
rie standard, et qui sont certainement un bon point de départ pour vous aider à trouver le style de
présentation qui vous convient.
— l’indentation
— les espaces
— les commentaires
w2-s5-c2-presentation 53
MOOC Python 3 Semaine 2, Séquence 5
Voici par exemple le code du module pprint (comme PrettyPrint) de la librairie standard qui permet
d’imprimer des données.
La fonction du module - le pretty printing - est évidemment accessoire ici, mais vous pouvez y voir
illustré
Espaces
Comme vous pouvez le voir dans [Link], les règles principales concernant les espaces sont les sui-
vantes.
w2-s5-c2-presentation 54
MOOC Python 3 Semaine 2, Séquence 5
Il est important de noter qu’il s’agit ici de règles d’usage et non pas de règles syntaxiques ; tous les
exemples barrés ci-dessus sont en fait syntaxiquement corrects, l’interpréteur les accepterait sans souci ;
mais ces règles sont très largement adoptées, et obligatoires pour intégrer du code dans la librairie
standard.
Coupures de ligne
Nous allons à présent zoomer dans ce module pour voir quelques exemples de coupure de ligne. Par
contraste avec ce qui précède, il s’agit cette fois surtout de règles syntaxiques, qui peuvent rendre un
code non valide si elles ne sont pas suivies.
[2]: show_module_html(pprint,
beg="def pprint",
end="def pformat")
La fonction pprint (ligne ~47) est une commodité (qui crée une instance de PrettyPrinter, sur lequel
on envoie la méthode pprint).
Vous voyez ici qu’il n’est pas nécessaire d’insérer un backslash (\) à la fin des lignes 50 et 51, car il y a
une parenthèse ouvrante qui n’est pas fermée à ce stade.
De manière générale, lorsqu’une parenthèse ouvrante ( - idem avec les crochets [ et accolades { - n’est
pas fermée sur la même ligne, l’interpréteur suppose qu’elle sera fermée plus loin et n’impose pas de
backslash.
valeurs = [
1,
2,
3,
5,
7,
]
Ou encore
x = un_nom_de_fonction_tres_tres_long(
argument1, argument2,
argument3, argument4,
)
À titre de rappel, signalons aussi les chaînes de caractères à base de """ ou ''' qui permettent elles
aussi d’utiliser plusieures lignes consécutives sans backslash, comme :
w2-s5-c2-presentation 55
MOOC Python 3 Semaine 2, Séquence 5
[3]: show_module_html(pprint,
beg="components), readable, recursive",
end="elif len(object) ",
lineno_width=3)
Dans ce fragment au contraire, vous voyez en ligne 521 qu’il a fallu cette fois insérer un backslash \
comme caractère de continuation pour que l’instruction puisse se poursuivre en ligne 522.
Dans tous les cas où une instruction est répartie sur plusieurs lignes, c’est naturellement l’indentation
de la première ligne qui est significative pour savoir à quel bloc rattacher cette instruction.
Notez bien enfin qu’on peut toujours mettre un backslash même lorsque ce n’est pas nécessaire, mais on
évite cette pratique en règle générale car les backslash nuisent à la lisibilité.
Il existe plusieurs outils liés à la PEP0008, pour vérifier si votre code est conforme, ou même le modifier
pour qu’il le devienne.
Ce qui nous donne un excellent prétexte pour parler un peu de [Link] qui est la
plateforme qui distribue les logiciels disponibles via l’outil pip3.
Les deux-points ‘ :’
Dans un autre registre entièrement, vous pouvez vous reporter à ce lien si vous êtes intéressé par la
question de savoir pourquoi on a choisi un délimiteur (le caractère deux-points :) pour terminer les
instructions comme if, for et def.
2.13 w2-s5-c3-pass
L’instruction pass
Nous avons vu qu’en Python les blocs de code sont définis par leur indentation.
w2-s5-c3-pass 56
MOOC Python 3 Semaine 2, Séquence 5
Cette convention a une limitation lorsqu’on essaie de définir un bloc vide. Voyons par exemple comment
on définirait en C une fonction qui ne fait rien :
Comme en Python on n’a pas d’accolade pour délimiter les blocs de code, il existe une instruction pass,
qui ne fait rien. À l’aide de cette instruction on peut à présent définir une fonction vide comme ceci :
Pour prendre un second exemple un peu plus pratique, et pour anticiper un peu sur l’instruction while
que nous verrons très bientôt, voici un exemple d’une boucle vide, c’est à dire sans corps, qui permet de
“dépiler” dans une liste jusqu’à l’obtention d’une certaine valeur :
avant [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
après [0, 1, 2, 3, 4]
Un if sans then
bingo
Et que l’on veuille modifier ce code pour simplement supprimer l’impression de non. La syntaxe du
langage ne permet pas de simplement commenter le premier print :
w2-s5-c3-pass 57
MOOC Python 3 Semaine 2, Séquence 6
Évidemment ceci pourrait être récrit autrement en inversant la condition, mais parfois on s’efforce de
limiter au maximum l’impact d’une modification sur le code. Dans ce genre de situation on préférera
écrire plutôt :
bingo
Enfin, comme on vient de le voir dans la vidéo, on peut aussi utiliser pass pour définir une classe vide
comme ceci :
2.14 w2-s6-c1-valeur-de-retour
Le style procédural
Une procédure est une fonction qui se contente de dérouler des instructions. Voici un exemple d’une telle
fonction :
[2]: affiche_carre(12)
w2-s6-c1-valeur-de-retour 58
MOOC Python 3 Semaine 2, Séquence 6
Le style fonctionnel
Mais en fait, dans notre cas, il serait beaucoup plus commode de définir une fonction qui retourne le
carré d’un nombre, afin de pouvoir écrire quelque chose comme :
surface = carre(15)
quitte à imprimer cette valeur ensuite si nécessaire. Jusqu’ici nous avons fait beaucoup appel à print,
mais dans la pratique, imprimer n’est pas un but en soi.
L’instruction return
Voici comment on pourrait écrire une fonction carre qui retourne (on dit aussi renvoie) le carré de son
argument :
petit appartement
La sémantique (le mot savant pour “comportement”) de l’instruction return est assez simple. La fonction
qui est en cours d’exécution s’achève immédiatement, et l’objet cité dans l’instruction return est retourné
à l’appelant, qui peut utiliser cette valeur comme n’importe quelle expression.
Le singleton None
Le terme même de fonction, si vous vous rappelez vos souvenirs de mathématiques, suggère qu’on calcule
un résultat à partir de valeurs d’entrée. Dans la pratique il est assez rare qu’on définisse une fonction
qui ne retourne rien.
En fait toutes les fonctions retournent quelque chose. Lorsque le programmeur n’a pas prévu d’instruction
return, Python retourne un objet spécial, baptisé None. Voici par exemple ce qu’on obtient si on essaie
d’afficher la valeur de retour de notre première fonction, qui, on le rappelle, ne retourne rien :
retour = None
L’objet None est un singleton prédéfini par Python, un peu comme True et False. Ce n’est pas par
contre une valeur booléenne, nous aurons l’occasion d’en reparler.
w2-s6-c1-valeur-de-retour 59
MOOC Python 3 Semaine 2, Séquence 6
Pour illustrer l’utilisation de return sur un exemple plus utile, voyons le code suivant :
Cette fonction teste si un entier est premier ou non ; il s’agit naturellement d’une version d’école, il existe
d’autres méthodes beaucoup plus adaptées à cette tâche. On peut toutefois vérifier que cette version est
fonctionnelle pour de petits entiers comme suit. On rappelle que 1 n’est pas considéré comme un nombre
premier :
premier(-2) = None
premier( 1) = False
premier( 2) = True
premier( 4) = False
premier(19) = True
premier(35) = False
Pour les besoins de cette discussion, nous avons choisi de retourner None pour les entiers négatifs ou nuls,
une manière comme une autre de signaler que la valeur en entrée n’est pas valide.
Ceci n’est pas forcément une bonne pratique, mais elle nous permet ici d’illustrer que dans le cas où on
ne mentionne pas de valeur de retour, Python retourne None.
w2-s6-c1-valeur-de-retour 60
MOOC Python 3 Semaine 2, Séquence 6
Comme on peut s’en convaincre en instrumentant le code - ce que vous pouvez faire à titre d’exercice en
ajoutant des fonctions print - dans le cas d’un nombre qui n’est pas premier la boucle for ne va pas
jusqu’à son terme.
On aurait pu d’ailleurs tirer profit de cette propriété pour écrire la fonction de manière légèrement
différente comme ceci :
C’est une question de style et de goût. En tout cas, les deux versions sont tout à fait équivalentes, comme
on le voit ici :
Vous remarquerez dans cette dernière cellule, si vous regardez bien le paramètre de print, qu’on peut
accoler deux chaînes (ici deux f-strings) sans même les ajouter ; un petit détail pour éviter d’alourdir le
code :
w2-s6-c1-valeur-de-retour 61
MOOC Python 3 Semaine 2, Séquence 6
[10]: 'abcdef'
2.15 w2-s6-x1-label
Elle devra retourner une chaîne de caractères selon que la note est
— 0 ⩽ note < 10
— 10 ⩽ note < 16
— 16 ⩽ note ⩽ 20
[2]: exo_label.example()
[ ]: # pour corriger
exo_label.correction(label)
# NOTE
# auto-exec-for-latex has skipped execution of this cell
w2-s6-x2-inconnue 62
MOOC Python 3 Semaine 2, Séquence 6
2.16 w2-s6-x2-inconnue
Séquences
Slicing
Commençons par créer une chaîne de caractères. Ne vous inquiétez pas si vous ne comprenez pas encore
le code d’initialisation utilisé ci-dessous.
Pour les plus curieux, l’instruction import permet de charger dans votre programme une boîte à outils que
l’on appelle un module. Python vient avec de nombreux modules qui forment la bibliothèque standard.
Le plus difficile avec les modules de la bibliothèque standard est de savoir qu’ils existent. En effet, il y
en a un grand nombre et bien souvent il existe un module pour faire ce que vous souhaitez.
# et voici sa valeur
print(chaine)
abcdefghijklmnopqrstuvwxyz
Pour chacune des sous-chaînes ci-dessous, écrire une expression de slicing sur chaine qui renvoie la
sous-chaîne. La cellule de code doit retourner True.
[2]: True
1) Écrivez une slice pour obtenir “vwx” (n’hésitez pas à utiliser les indices négatifs) :
# NOTE
# auto-exec-for-latex has skipped execution of this cell
# NOTE
# auto-exec-for-latex has skipped execution of this cell
w2-s6-x2-inconnue 63
MOOC Python 3 Semaine 2, Séquence 6
# NOTE
# auto-exec-for-latex has skipped execution of this cell
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Longueur
On vous donne une chaîne composite dont on sait qu’elle a été calculée à partir de deux chaînes inconnue
et connue comme ceci :
On vous donne également la chaîne connue. Imaginez par exemple que vous avez (ce ne sont pas les
vraies valeurs) :
connue = '0bf1'
composite = '0bf1a9730e150bf1'
inconnue = 'a9730e15'
L’exercice consiste à écrire une fonction qui retourne la valeur de inconnue à partir de celles de composite
et connue. Vous pouvez admettre que connue n’est pas vide, c’est-à-dire qu’elle contient au moins un
caractère.
Vous pouvez utiliser du slicing, et la fonction len(), qui retourne la longueur d’une chaîne :
[4]: len('abcd')
[4]: 4
w2-s6-x2-inconnue 64
MOOC Python 3 Semaine 2, Séquence 6
Une fois votre code évalué, vous pouvez évaluer la cellule suivante pour vérifier votre résultat.
[ ]: # correction
exo_inconnue.correction(inconnue)
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Si toutes les lignes sont en vert c’est que vous avez réussi cet exercice.
Vous pouvez essayer autant de fois que vous voulez, mais il vous faut alors à chaque itération :
2.17 w2-s6-x3-laccess
Listes
Vous devez écrire une fonction laccess qui prend en argument une liste, et qui retourne :
[2]: exo_laccess.example()
[ ]: # pour le corriger
exo_laccess.correction(laccess)
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Une fois que votre code fonctionne, vous pouvez regarder si par hasard il marcherait aussi avec des
chaînes :
w2-s6-x3-laccess 65
MOOC Python 3 Semaine 2, Séquence 6
[ ]: exo_laccess_strings.correction(laccess)
# NOTE
# auto-exec-for-latex has skipped execution of this cell
2.18 w2-s6-x4-if-et-def
Fonction de divisibilité
L’exercice consiste à écrire une fonction baptisée divisible qui retourne une valeur booléenne, qui
indique si un des deux arguments est divisible par l’autre.
Vous pouvez supposer les entrées a et b entiers et non nuls, mais pas forcément positifs.
Vous pouvez à présent tester votre code en évaluant ceci, qui écrira un message d’erreur si un des jeux
de test ne donne pas le résultat attendu.
# NOTE
# auto-exec-for-latex has skipped execution of this cell
On veut définir en Python une fonction qui est définie par morceaux :
− x − 5 si x ⩽ −5
f : x −→ 0 si x ∈ [−5, 5]
1
5 x − 1 si x ⩾ 5
w2-s6-x4-if-et-def 66
MOOC Python 3 Semaine 2, Séquence 6
def morceaux(x):
return 0 # "votre code"
# NOTE
# auto-exec-for-latex has skipped execution of this cell
Représentation graphique
L’exercice est terminé, mais nous allons maintenant voir ensemble comment vous pourriez visualiser votre
fonction.
Voici ce qui est attendu comme courbe pour morceaux (image fixe) :
En partant de votre code, vous pouvez produire votre propre courbe en utilisant numpy et matplotlib
comme ceci :
w2-s6-x4-if-et-def 67
MOOC Python 3 Semaine 2, Séquence 6
# et les Y correspondants
Y = [Link](morceaux)(X)
2.19 w2-s6-x5-wc
Sur les systèmes de type UNIX, la commande wc permet de compter le nombre de lignes, de mots et
d’octets (ou de caractères) présents sur l’entrée standard ou contenus dans un fichier.
L’exercice consiste à écrire une fonction nommée wc qui prendra en argument une chaîne de caractères
et retournera une liste contenant trois éléments :
w2-s6-x5-wc 68
MOOC Python 3 Semaine 2, Séquence 7
[2]: # exemple
exo_wc.example()
Indice : nous avons vu rapidement la boucle for, sachez toutefois qu’on peut tout à fait résoudre l’exercice
en utilisant uniquement la bibliothèque standard.
Remarque : usuellement, ce genre de fonctions retournerait plutôt un tuple qu’une liste, mais comme
nous ne voyons les tuples que la semaine prochaine..
À vous de jouer :
[ ]: # correction
exo_wc.correction(wc)
# NOTE
# auto-exec-for-latex has skipped execution of this cell
2.20 w2-s7-x1-liste-p
Compréhensions (1)
P( x ) = 2x2 − 3x − 2
On vous demande d’écrire une fonction liste_P qui prend en argument une liste de nombres réels x et
qui retourne la liste des valeurs P( x ).
w2-s7-x1-liste-p 69
MOOC Python 3 Semaine 2, Séquence 7
Écrivez votre code dans la cellule suivante (On vous suggère d’écrire une fonction P qui implémente le
polynôme mais ça n’est pas strictement indispensable, seul le résultat de liste_P compte) :
def liste_P(liste_x):
"votre code"
# NOTE:
# auto-exec-for-latex has used hidden code instead
# NOTE
# auto-exec-for-latex has skipped execution of this cell
2.20.2 Récréation
Si vous avez correctement implémenté la fonction liste_P telle que demandé dans le premier exercice,
vous pouvez visualiser le polynôme P en utilisant matplotlib avec le code suivant :
# et les Y correspondants
Y = liste_P(X)
w2-s7-x1-liste-p 70
MOOC Python 3 Semaine 2, Séquence 7
2.21 w2-s7-x2-carre
Compréhensions (2)
Mise au carré
On vous demande à présent d’écrire une fonction dans le même esprit que la fonction polynomiale du
notebook précédent. Cette fois, chaque ligne contient, séparés par des points-virgules, une liste d’entiers,
et on veut obtenir une nouvelle chaîne avec les carrés de ces entiers, séparés par des deux-points.
À nouveau les lignes peuvent être remplies de manière approximative, avec des espaces, des tabulations,
ou même des points-virgules en trop, que ce soit au début, à la fin, ou au milieu d’une ligne.
[2]: # exemples
exo_carre.example()
w2-s7-x2-carre 71
MOOC Python 3 Semaine 2, Séquence 7
[ ]: # pour corriger
exo_carre.correction(carre)
# NOTE
# auto-exec-for-latex has skipped execution of this cell
w2-s7-x2-carre 72