0% ont trouvé ce document utile (0 vote)
2 vues141 pages

Cours-Python Chapitre 2

Ce document est un cours d'introduction à la programmation Python, spécifiquement conçu pour les étudiants en biologie à l'Université Paris Cité. Il couvre divers aspects de Python, y compris les fonctions, les chaînes de caractères, les listes et les conteneurs, avec des exercices pratiques pour renforcer l'apprentissage. Le contenu est sous licence Creative Commons, permettant un partage et une adaptation sous certaines conditions.

Transféré par

Sim prod
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
2 vues141 pages

Cours-Python Chapitre 2

Ce document est un cours d'introduction à la programmation Python, spécifiquement conçu pour les étudiants en biologie à l'Université Paris Cité. Il couvre divers aspects de Python, y compris les fonctions, les chaînes de caractères, les listes et les conteneurs, avec des exercices pratiques pour renforcer l'apprentissage. Le contenu est sous licence Creative Commons, permettant un partage et une adaptation sous certaines conditions.

Transféré par

Sim prod
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Cours de Python

Introduction à la programmation Python pour la biologie


[Link]

Patrick Fuchs et Pierre Poulain


prénom [point] nom [arobase] u-paris [point] fr

version du 29 septembre 2025

Université Paris Cité, France

Ce document est sous licence


Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 France
(CC BY-SA 3.0 FR)
[Link]
Table des matières Table des matières

9.8 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

10 Fonctions 88
10.1 Principe et généralités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
10.2 Définition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
10.3 Passage d’arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
10.4 Renvoi de résultats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
10.5 Arguments positionnels et arguments par mot-clé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10.6 Variables locales et variables globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
10.7 Principe DRY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
10.8 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

11 Plus sur les chaînes de caractères 102


11.1 Préambule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
11.2 Chaînes de caractères et listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
11.3 Caractères spéciaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
11.4 Préfixe de chaîne de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
11.5 Méthodes associées aux chaînes de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
11.6 Extraction de valeurs numériques d’une chaîne de caractères . . . . . . . . . . . . . . . . . . . . . . . . 107
11.7 Fonction map() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
11.8 Test d’appartenance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
11.9 Conversion d’une liste de chaînes de caractères en une chaîne de caractères . . . . . . . . . . . . . . . . 108
11.10Method chaining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
11.11Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

12 Plus sur les listes 116


12.1 Méthodes associées aux listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
12.2 Construction d’une liste par itération . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
12.3 Test d’appartenance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
12.4 Fonction zip() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
12.5 Copie de listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
12.6 Initialisation d’une liste de listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
12.7 Liste de compréhension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
12.8 Tris puissants de listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
12.9 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

13 Plus sur les fonctions 129


13.1 Appel d’une fonction dans une fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
13.2 Fonctions récursives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
13.3 Portée des variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
13.4 Portée des listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
13.5 Règle LGI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
13.6 Recommandations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
13.7 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136

14 Conteneurs 138
14.1 Généralités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
14.2 Plus sur les dictionnaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
14.3 Plus sur les tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
14.4 Sets et frozensets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
14.5 Récapitulation des propriétés des conteneurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
14.6 Dictionnaires et sets de compréhension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
14.7 Module collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
14.8 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154

4 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


Table des matières Table des matières

15 Création de modules 158


15.1 Pourquoi créer ses propres modules ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
15.2 Création d’un module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
15.3 Utilisation de son propre module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
15.4 Les docstrings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
15.5 Visibilité des fonctions dans un module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
15.6 Module ou script ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
15.7 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

16 Bonnes pratiques en programmation Python 163


16.1 De la bonne syntaxe avec la PEP 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
16.2 Les docstrings et la PEP 257 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
16.3 Outils de contrôle qualité du code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
16.4 Outil de formatage automatique du code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
16.5 Organisation du code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
16.6 Conseils sur la conception d’un script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
16.7 Pour terminer : la PEP 20 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174

17 Expressions régulières et parsing 176


17.1 Définition et syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
17.2 Quelques ressources en ligne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
17.3 Le module re . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
17.4 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181

18 Jupyter et ses notebooks 184


18.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
18.2 JupyterLab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
18.3 Création d’un notebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
18.4 Le format Markdown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
18.5 Des graphiques dans les notebooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
18.6 Les magic commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
18.7 Lancement d’une commande Unix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191

19 Module Biopython 194


19.1 Installation et convention . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
19.2 Chargement du module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
19.3 Manipulation de séquences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
19.4 Interrogation de la base de données PubMed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
19.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198

20 Module NumPy 201


20.1 Installation et convention . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
20.2 Chargement du module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
20.3 Objets de type array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
20.4 Construction automatique de matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
20.5 Chargement d’un array depuis un fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
20.6 Concaténation d’arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
20.7 Un peu d’algèbre linéaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
20.8 Parcours de matrice et affectation de lignes et colonnes . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
20.9 Masques booléens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
20.10Quelques conseils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
20.11Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 5


CHAPITRE 10

Fonctions

10.1 Principe et généralités


En programmation, les fonctions sont très utiles pour réaliser plusieurs fois la même opération au sein d’un programme.
Elles rendent également le code plus lisible et plus clair en le fractionnant en blocs logiques.
Vous connaissez déjà certaines fonctions Python. Par exemple [Link](angle) du module math renvoie le cosinus
de la variable angle exprimé en radian. Vous connaissez aussi des fonctions internes à Python comme range() ou
len(). Pour l’instant, une fonction est à vos yeux une sorte de « boîte noire » :
1. À laquelle vous passez aucune, une ou plusieurs variable(s) entre parenthèses. Ces variables sont appelées argu-
ments. Il peut s’agir de n’importe quel type d’objet Python.
2. Qui effectue une action.
3. Qui renvoie un objet Python ou rien du tout.
Tout cela est illustré schématiquement dans la figure ci-dessous.

Figure 10.1 – Fonctionnement schématique d’une fonction.

Par exemple, si vous appelez la fonction len() de la manière suivante :

88
10.2. Définition Chapitre 10. Fonctions

1 >>> len([0, 1, 2])


2 3

voici ce qu’il se passe :


1. vous appelez len() en lui passant une liste en argument (ici la liste [0, 1, 2]) ;
2. la fonction calcule la longueur de cette liste ;
3. elle vous renvoie un entier égal à cette longueur.
Autre exemple, si vous appelez la méthode ma_liste.append() (n’oubliez pas, une méthode est une fonction qui
agit sur l’objet auquel elle est attachée par un point) :
1 >>> ma_liste.append(5)

1. Vous passez l’entier 5 en argument ;


2. la méthode append() ajoute l’entier 5 à l’objet ma_liste ;
3. et elle ne renvoie rien.
Aux yeux du programmeur, au contraire, une fonction est une portion de code effectuant une suite d’instructions bien
particulière. Mais avant de vous présenter la syntaxe et la manière de construire une fonction, revenons une dernière fois
sur cette notion de « boîte noire » :
• Une fonction effectue une tâche. Pour cela, elle reçoit éventuellement des arguments et renvoie éventuellement
quelque chose. L’algorithme utilisé au sein de la fonction n’intéresse pas directement l’utilisateur. Par exemple, il
est inutile de savoir comment la fonction [Link]() calcule un cosinus. On a juste besoin de savoir qu’il faut lui
passer en argument un angle en radian, et qu’elle renvoie le cosinus de cet angle. Ce qui se passe à l’intérieur de
la fonction ne regarde que le programmeur.
• Chaque fonction effectue en général une tâche unique et précise. Si cela se complique, il est plus judicieux d’écrire
plusieurs fonctions (qui peuvent éventuellement s’appeler les unes les autres). Cette modularité améliore la qualité
générale et la lisibilité du code. Vous verrez qu’en Python, les fonctions présentent une grande flexibilité.
Pour finir sur les généralités, nous avons utilisé dans la Figure ci-dessus le terme programme principal (main en
anglais), pour désigner l’endroit depuis lequel on appelle une fonction (on verra plus tard que l’on peut en fait appeler
une fonction de n’importe où). Le programme principal désigne le code qui est exécuté lorsqu’on lance le script Python,
c’est-à-dire toute la suite d’instructions en dehors des fonctions. En général, dans un script Python, on écrit d’abord les
fonctions, puis le programme principal. Nous aurons l’occasion de revenir sur cette notion de programme principal plus
tard dans ce chapitre, ainsi que dans le chapitre 13 Plus sur les fonctions.

10.2 Définition
Pour définir une fonction, Python utilise le mot-clé def. Si on souhaite que la fonction renvoie quelque chose, il faut
utiliser le mot-clé return. Par exemple :
1 >>> def carre(x):
2 ... return x**2
3 ...
4 >>> print(carre(2))
5 4

Notez que la syntaxe de def utilise les deux-points comme les boucles for et while ainsi que les tests if : un bloc
d’instructions est donc attendu. De même que pour les boucles et les tests, l’indentation de ce bloc d’instructions (qu’on
appelle le corps de la fonction) est obligatoire.
Dans l’exemple précédent, nous avons passé un argument à la fonction carre(), qui nous a renvoyé (ou retourné)
une valeur que nous avons immédiatement affichée à l’écran avec l’instruction print(). Que veut dire valeur renvoyée ?
Et bien cela signifie que cette dernière est récupérable dans une variable :
1 >>> res = carre(2)
2 >>> print(res)
3 4

Ici, le résultat renvoyé par la fonction est stocké dans la variable res. Notez qu’une fonction ne prend pas forcément
un argument et ne renvoie pas forcément une valeur, par exemple :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 89


Chapitre 10. Fonctions 10.3. Passage d’arguments

1 >>> def hello():


2 ... print("bonjour")
3 ...
4 >>> hello()
5 bonjour

Dans ce cas, la fonction hello() se contente d’afficher la chaîne de caractères "bonjour" à l’écran. Elle ne prend
aucun argument et ne renvoie rien. Par conséquent, cela n’a pas de sens de vouloir récupérer dans une variable le résultat
renvoyé par une telle fonction. Si on essaie tout de même, Python affecte la valeur None qui signifie rien en anglais :
1 >>> var = hello()
2 bonjour
3 >>> print(var)
4 None

Ceci n’est pas une faute car Python n’émet pas d’erreur, toutefois cela ne présente, la plupart du temps, guère
d’intérêt.

10.3 Passage d’arguments


Le nombre d’arguments que l’on peut passer à une fonction est variable. Nous avons vu ci-dessus des fonctions
auxquelles on passait zero ou un argument. Dans les chapitres précédents, vous avez rencontré des fonctions internes à
Python qui prenaient au moins deux arguments. Souvenez-vous par exemple de range(1, 10) ou encore range(1,
10, 2). Le nombre d’arguments est donc laissé libre à l’initiative du programmeur qui développe une nouvelle fonction.
Une particularité des fonctions en Python est que vous n’êtes pas obligé de préciser le type des arguments que vous
lui passez, dès lors que les opérations que vous effectuez avec ces arguments sont valides. Python est en effet connu
comme étant un langage au « typage dynamique », c’est-à-dire qu’il reconnaît pour vous le type des variables au moment
de l’exécution. Par exemple :
1 >>> def fois(x, y):
2 ... return x*y
3 ...
4 >>> fois(2, 3)
5 6
6 >>> fois(3.1415, 5.23)
7 16.430045000000003
8 >>> fois("to", 2)
9 'toto'
10 >>> fois([1,3], 2)
11 [1, 3, 1, 3]

L’opérateur * reconnaît plusieurs types (entiers, floats, chaînes de caractères, listes). Notre fonction fois() est donc
capable d’effectuer des tâches différentes ! Même si Python autorise cela, méfiez-vous tout de même de cette grande
flexibilité qui pourrait conduire à des surprises dans vos futurs programmes. En général, il est plus judicieux que chaque
argument ait un type précis (entiers, floats, chaînes de caractères, etc.) et pas l’un ou l’autre.

10.4 Renvoi de résultats


Un énorme avantage en Python est que les fonctions sont capables de renvoyer plusieurs objets à la fois, comme dans
cette fraction de code :
1 >>> def carre_cube(x):
2 ... return x**2, x**3
3 ...
4 >>> carre_cube(2)
5 (4, 8)

En réalité Python ne renvoie qu’un seul objet, mais celui-ci peut être séquentiel, c’est-à-dire contenir lui-même
d’autres objets. Dans notre exemple, Python renvoie un objet de type tuple, type que nous avons vu dans le chapitre
8 Dictionnaires et tuples (souvenez-vous, il s’agit d’une sorte de liste avec des propriétés différentes). Notre fonction
pourrait tout autant renvoyer une liste :

90 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


10.5. Arguments positionnels et arguments par mot-clé Chapitre 10. Fonctions

1 >>> def carre_cube2(x):


2 ... return [x**2, x**3]
3 ...
4 >>> carre_cube2(3)
5 [9, 27]

Renvoyer un tuple ou une liste de deux éléments (ou plus) est très pratique en conjonction avec l’affectation multiple,
par exemple :
1 >>> z1, z2 = carre_cube2(3)
2 >>> z1
3 9
4 >>> z2
5 27

Cela permet de récupérer plusieurs valeurs renvoyées par une fonction et de les affecter à la volée à des variables
différentes.
Une fonction peut aussi renvoyer un booléen :
1 def est_pair(x):
2 if x % 2 == 0:
3 return True
4 else:
5 return False
6
7 # Programme principal.
8 for chiffre in range(1, 5):
9 if est_pair(chiffre):
10 print(f"{chiffre} est pair")

Comme la fonction renvoie un booléen, on peut utiliser la notation if est_pair(chiffre): qui équivaut à if
est_pair(chiffre) == True:. Il est courant d’appeler une fonction qui renvoie un booléen est_quelquechose()
car on comprend que ça pose la question si c’est vrai ou faux. En anglais, on trouvera la notation is_even(). Nous
reverrons ces notions dans le chapitre 13 Plus sur les fonctions.

10.5 Arguments positionnels et arguments par mot-clé


Jusqu’à maintenant, nous avons systématiquement passé le nombre d’arguments que la fonction attendait. Que se
passe-t-il si une fonction attend deux arguments et que nous ne lui en passons qu’un seul ?
1 >>> def fois(x, y):
2 ... return x*y
3 ...
4 >>> fois(2, 3)
5 6
6 >>> fois(2)
7 Traceback (most recent call last):
8 File "<stdin>", line 1, in <module>
9 TypeError: fois() missing 1 required positional argument: 'y'

On constate que passer un seul argument à une fonction qui en attend deux conduit à une erreur.

Définition
Lorsqu’on définit une fonction def fct(x, y): les arguments x et y sont appelés arguments positionnels (en
anglais, positional arguments). Il est strictement obligatoire de les préciser lors de l’appel de la fonction. De plus, il est
nécessaire de respecter le même ordre lors de l’appel que dans la définition de la fonction. Dans l’exemple ci-dessus, 2
correspondra à x et 3 correspondra à y. Finalement, tout dépendra de leur position, d’où leur qualification de positionnel.

Mais il est aussi possible de passer un ou plusieurs argument(s) de manière facultative et de leur attribuer une valeur
par défaut :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 91


Chapitre 10. Fonctions 10.5. Arguments positionnels et arguments par mot-clé

1 >>> def fct(x=1):


2 ... return x
3 ...
4 >>> fct()
5 1
6 >>> fct(10)
7 10

Définition
Un argument défini avec une syntaxe def fct(arg=val): est appelé argument par mot-clé (en anglais, keyword
argument). Le passage d’un tel argument lors de l’appel de la fonction est facultatif. Ce type d’argument ne doit pas être
confondu avec les arguments positionnels présentés ci-dessus, dont la syntaxe est def fct(arg):.

Il est bien sûr possible de passer plusieurs arguments par mot-clé :


1 >>> def fct(x=0, y=0, z=0):
2 ... return x, y, z
3 ...
4 >>> fct()
5 (0, 0, 0)
6 >>> fct(10)
7 (10, 0, 0)
8 >>> fct(10, 8)
9 (10, 8, 0)
10 >>> fct(10, 8, 3)
11 (10, 8, 3)

On observe que pour l’instant, les arguments par mot-clé sont pris dans l’ordre dans lesquels on les passe lors de
l’appel. Comment faire si l’on souhaitait préciser l’argument par mot-clé z et garder les valeurs de x et y par défaut ?
Simplement en précisant le nom de l’argument lors de l’appel :
1 >>> fct(z=10)
2 (0, 0, 10)

Python permet même de rentrer les arguments par mot-clé dans un ordre arbitraire :
1 >>> fct(z=10, x=3, y=80)
2 (3, 80, 10)
3 >>> fct(z=10, y=80)
4 (0, 80, 10)

Que se passe-t-il lorsque nous avons un mélange d’arguments positionnels et par mot-clé ? Et bien les arguments
positionnels doivent toujours être placés avant les arguments par mot-clé :
1 >>> def fct(a, b, x=0, y=0, z=0):
2 ... return a, b, x, y, z
3 ...
4 >>> fct(1, 1)
5 (1, 1, 0, 0, 0)
6 >>> fct(1, 1, z=5)
7 (1, 1, 0, 0, 5)
8 >>> fct(1, 1, z=5, y=32)
9 (1, 1, 0, 32, 5)

On peut toujours passer les arguments par mot-clé dans un ordre arbitraire à partir du moment où on précise leur
nom. Par contre, si les deux arguments positionnels a et b ne sont pas passés à la fonction, Python renvoie une erreur.
1 >>> fct(z=0)
2 Traceback (most recent call last):
3 File "<stdin>", line 1, in <module>
4 TypeError: fct() missing 2 required positional arguments: 'a' and 'b'

92 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


10.6. Variables locales et variables globales Chapitre 10. Fonctions

Conseil
Préciser le nom des arguments par mot-clé lors de l’appel d’une fonction est une pratique que nous vous recommandons.
Cela les distingue clairement des arguments positionnels.

L’utilisation d’arguments par mot-clé est habituelle en Python. Elle permet de modifier le comportement par défaut
de nombreuses fonctions. Par exemple, si on souhaite que la fonction print() n’affiche pas un retour à la ligne, on peut
utiliser l’argument end :
1 >>> print("Message ", end="")
2 Message >>>

Nous verrons, dans le chapitre 25 Fenêtres graphiques et Tkinter (en ligne), que l’utilisation d’arguments par mot-clé
est systématique lorsqu’on crée un objet graphique (une fenêtre, un bouton, etc.).

10.6 Variables locales et variables globales


Lorsqu’on manipule des fonctions, il est essentiel de bien comprendre comment se comportent les variables. Une
variable est dite locale lorsqu’elle est créée dans une fonction. Elle n’existera et ne sera visible que lors de l’exécution de
ladite fonction.
Une variable est dite globale lorsqu’elle est créée dans le programme principal. Elle sera visible partout dans le
programme.
Ceci ne vous paraît pas clair ? Nous allons prendre un exemple simple qui vous aidera à mieux saisir ces concepts.
Observez le code suivant :
1 # Définition d'une fonction carre().
2 def carre(x):
3 y = x**2
4 return y
5
6 # Programme principal.
7 var = 5
8 resultat = carre(var)
9 print(resultat)

Pour la suite des explications, nous allons utiliser l’excellent site Python Tutor 1 qui permet de visualiser l’état des
variables au fur et à mesure de l’exécution d’un code Python. Avant de poursuivre, nous vous conseillons de prendre 5
minutes pour tester ce site.
Regardons maintenant ce qui se passe dans le code ci-dessus, étape par étape :
• Étape 1 : Python est prêt à lire la première ligne de code.

1. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 93


Chapitre 10. Fonctions 10.6. Variables locales et variables globales

• Étape 2 : Python met en mémoire la fonction carre(). Notez qu’il ne l’exécute pas ! La fonction est mise dans
un espace de la mémoire nommé Global frame, il s’agit de l’espace du programme principal. Dans cet espace
seront stockées toutes les variables globales créées dans le programme. Python est maintenant prêt à exécuter le
programme principal.

• Étape 3 : Python lit et met en mémoire la variable var. Celle-ci étant créée dans le programme principal, il s’agira
d’une variable globale. Ainsi, elle sera également stockée dans le Global frame.

• Étape 4 : La fonction carre() est appelée et on lui passe en argument l’entier var. La fonction s’exécute et
un nouveau cadre est créé dans lequel Python Tutor va indiquer toutes les variables locales à la fonction. Notez
bien que la variable passée en argument, qui s’appelle x dans la fonction, est créée en tant que variable locale. On
remarquera aussi que les variables globales situées dans le Global frame sont toujours là.

94 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


10.6. Variables locales et variables globales Chapitre 10. Fonctions

• Étape 5 : Python est maintenant prêt à exécuter chaque ligne de code de la fonction.

• Étape 6 : La variable y est créée dans la fonction. Celle-ci est donc stockée en tant que variable locale à la fonction.

• Étape 7 : Python s’apprête à renvoyer la variable locale y au programme principal. Python Tutor nous indique le
contenu de la valeur renvoyée.

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 95


Chapitre 10. Fonctions 10.6. Variables locales et variables globales

• Étape 8 : Python quitte la fonction et la valeur renvoyée par celle-ci est affectée à la variable globale resultat.
Notez bien que lorsque Python quitte la fonction, l’espace des variables alloué à la fonction est détruit. Ainsi,
toutes les variables créées dans la fonction n’existent plus. On comprend pourquoi elles portent le nom de locales
puisqu’elles n’existent que lorsque la fonction est exécutée.

• Étape 9 : Python affiche le contenu de la variable resultat et l’exécution est terminée.

Nous espérons que cet exemple guidé facilitera la compréhension des concepts de variables locales et globales. Cela
viendra aussi avec la pratique. Nous irons un peu plus loin sur les fonctions dans le chapitre 13 Plus sur les fonctions.

96 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


10.7. Principe DRY Chapitre 10. Fonctions

D’ici là, essayez de vous entraîner au maximum avec les fonctions. C’est un concept ardu, mais il est impératif de le
maîtriser.
Enfin, comme vous avez pu le constater, Python Tutor nous a grandement aidé à comprendre ce qui se passait.
N’hésitez pas à l’utiliser sur des exemples ponctuels, ce site vous aidera à visualiser ce qui se passe lorsqu’un code ne fait
pas ce que vous attendez.

10.7 Principe DRY


L’acronyme DRY 2 signifie Don’t Repeat Yourself. Les fonctions permettent de satisfaire ce principe en évitant la
duplication de code. En effet, plus un code est dupliqué plusieurs fois dans un programme, plus il sera source d’erreurs,
notamment lorsqu’il faudra le faire évoluer.
Considérons par exemple le code suivant qui convertit plusieurs températures des degrés Fahrenheit en degrés Celsius :
1 >>> temp_in_fahrenheit = 60
2 >>> (temp_in_fahrenheit - 32) * (5/8)
3 17.5
4 >>> temp_in_fahrenheit = 80
5 >>> (temp_in_fahrenheit - 32) * (5/8)
6 30.0
7 >>> temp_in_fahrenheit = 100
8 >>> (temp_in_fahrenheit - 32) * (5/8)
9 42.5

Malheureusement, il y a une erreur dans la formule de conversion. En effet, la formule exacte est :

5
temp_celsius = (temp_fahrenheit − 32) ×
9
Il faut alors reprendre les lignes 2, 5 et 8 précédentes et les corriger. Cela n’est pas efficace, surtout si le même code
est utilisé à différents endroits dans le programme.
En écrivant qu’une seule fois la formule de conversion dans une fonction, on applique le principe DRY :
1 >>> def convert_fahrenheit_to_celsius(temperature):
2 ... return (temperature - 32) * (5/9)
3 ...
4 >>> temp_in_fahrenheit = 60
5 >>> convert_fahrenheit_to_celsius(temp_in_fahrenheit)
6 15.555555555555557
7 >>> temp_in_fahrenheit = 80
8 >>> convert_fahrenheit_to_celsius(temp_in_fahrenheit)
9 26.666666666666668
10 >>> temp_in_fahrenheit = 100
11 >>> convert_fahrenheit_to_celsius(temp_in_fahrenheit)
12 37.77777777777778

Et s’il y a une erreur dans la formule, il suffira de ne la corriger qu’une seule fois, dans la fonction convert_fahrenheit_to_celsius
().

10.8 Exercices

Conseil
Pour le premier exercice, utilisez Python Tutor. Pour les exercices suivants, créez des scripts puis exécutez-les dans
un shell.

10.8.1 Carré et factorielle


Reprenez l’exemple précédent à l’aide du site Python Tutor 3 :
2. [Link]
intro-to-clean-code/dry-modular-code/
3. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 97


Chapitre 10. Fonctions 10.8. Exercices

1 # Définition d'une fonction carre().


2 def carre(x):
3 y = x**2
4 return y
5
6 # Programme principal.
7 z = 5
8 resultat = carre(z)
9 print(resultat)

Analysez ensuite le code suivant et tentez de prédire sa sortie :


1 def calc_factorielle(n):
2 fact = 1
3 for i in range(2, n+1):
4 fact = fact * i
5 return fact
6
7 # Programme principal.
8 nb = 4
9 factorielle_nb = calc_factorielle(nb)
10 print(f"{nb}! = {factorielle_nb}")
11 nb2 = 10
12 print(f"{nb2}! = {calc_factorielle(nb2)}")

Testez ensuite cette portion de code avec Python Tutor, en cherchant à bien comprendre chaque étape. Avez-vous
réussi à prédire la sortie correctement ?

Remarque
Une remarque concernant l’utilisation des f-strings que nous avions abordées dans le chapitre 3 Affichage. On découvre
ici une autre possibilité des f-strings dans l’instruction f"{nb2}! = {calc_factorielle(nb2)}" : il est en effet
possible d’appeler entre les accolades une fonction (ici {calc_factorielle(nb2)}) ! Ainsi, il n’est pas nécessaire de
créer une variable intermédiaire dans laquelle on stocke ce que retourne la fonction.

10.8.2 Puissance
Créez une fonction calc_puissance(x, y) qui renvoie xy en utilisant l’opérateur **. Pour rappel :
1 >>> 2**2
2 4
3 >>> 2**3
4 8
5 >>> 2**4
6 16

Dans le programme principal, calculez et affichez à l’écran 2i avec i variant de 0 à 20 inclus. On souhaite que le
résultat soit présenté avec le formatage suivant :
2^ 0 = 1
2^ 1 = 2
2^ 2 = 4
[...]
2^20 = 1048576

10.8.3 Pyramide
Reprenez l’exercice du chapitre 5 Boucles et comparaisons qui dessine une pyramide.
Dans un script [Link], créez une fonction gen_pyramide() à laquelle vous passez un nombre entier N et qui
renvoie une pyramide de N lignes sous forme de chaîne de caractères. Le programme principal demandera à l’utilisateur
le nombre de lignes souhaitées (utilisez pour cela la fonction input()) et affichera la pyramide à l’écran.

98 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


10.8. Exercices Chapitre 10. Fonctions

10.8.4 Nombres premiers


Reprenez l’exercice du chapitre 6 Tests sur les nombres premiers.
Créez une fonction est_premier() qui prend comme argument un nombre entier positif n (supérieur à 2), et qui
renvoie le booléen True si n est premier et False si n n’est pas premier. Déterminez tous les nombres premiers de 2 à
100. On souhaite avoir une sortie similaire à celle-ci :
2 est premier
3 est premier
4 n'est pas premier
[...]
100 n'est pas premier

10.8.5 Séquence complémentaire


Créez une fonction seq_comp() qui prend comme argument une liste de bases et qui renvoie la séquence complé-
mentaire d’une séquence d’ADN sous forme de liste.
Dans le programme principal, à partir de la séquence d’ADN
seq = ["A", "T", "C", "G", "A", "T", "C", "G", "A", "T", "C"]
affichez seq et sa séquence complémentaire (en utilisant votre fonction seq_comp()).
Rappel : la séquence complémentaire s’obtient en remplaçant A par T, T par A, C par G et G par C.

10.8.6 Distance 3D
Créez une fonction calc_distance_3D() qui calcule la distance euclidienne en√trois dimensions entre deux atomes.
Testez votre fonction sur les 2 points A(0,0,0) et B(1,1,1). Trouvez-vous bien 3 ?
On rappelle que la distance euclidienne d entre deux points A et B de coordonnées cartésiennes respectives (xA , yA , zA )
et (xB , yB , zB ) se calcule comme suit :

d= (xB − xA )2 + (yB − yA )2 + (zB − zA )2

10.8.7 Distribution et statistiques


Créez une fonction gen_distrib() qui prend comme argument trois entiers : debut, fin et n. La fonction renverra
une liste de n floats aléatoires entre debut et fin. Pour générer un nombre aléatoire dans un intervalle donné, utilisez la
fonction uniform() du module random, dont voici quelques exemples d’utilisation :
1 >>> import random
2 >>> [Link](1, 10)
3 8.199672607202174
4 >>> [Link](1, 10)
5 2.607528561528022
6 >>> [Link](1, 10)
7 9.000404025130946

Avec la fonction [Link](), les bornes passées en argument sont incluses, c’est-à-dire qu’ici, le nombre
aléatoire renvoyé est dans l’intervalle [1, 10].
Créez une autre fonction calc_stat() qui prend en argument une liste de floats et qui renvoie une liste de trois
éléments contenant respectivement le minimum, le maximum et la moyenne de la liste.
Dans le programme principal, générez 20 listes aléatoires de 100 floats compris entre 0 et 100 et affichez le minimum
(min()), le maximum (max()) et la moyenne pour chacune d’entre elles. La moyenne pourra être calculée avec les
fonctions sum() et len().
Pour chacune des 20 listes, affichez les statistiques (valeur minimale, valeur maximale et moyenne) avec deux chiffres
après la virgule :
Liste 1 : min = 0.17 ; max = 99.72 ; moyenne = 57.38
Liste 2 : min = 1.25 ; max = 99.99 ; moyenne = 47.41
[...]
Liste 19 : min = 1.05 ; max = 99.36 ; moyenne = 49.43
Liste 20 : min = 1.33 ; max = 97.63 ; moyenne = 46.53

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 99


Chapitre 10. Fonctions 10.8. Exercices

Les écarts sur les statistiques entre les différentes listes sont-ils importants ? Relancez votre script avec des listes de
1000 éléments, puis 10 000 éléments. Les écarts changent-ils quand le nombre d’éléments par liste augmente ?

10.8.8 Distance à l’origine (exercice +++)

En reprenant votre fonction de calcul de distance euclidienne en trois dimensions calc_distance_3D(), faites-en
une version pour deux dimensions que vous appellerez calc_distance_2D().

Créez une autre fonction calc_dist2ori(), à laquelle vous passez en argument deux listes de floats list_x et
list_y représentant les coordonnées d’une fonction mathématique (par exemple x et sin(x)). Cette fonction renverra
une liste de floats représentant la distance entre chaque point de la fonction et l’origine (de coordonnées (0, 0)).

La figure 10.2 montre un exemple sur quelques points de la fonction sin(x) (courbe en trait épais). Chaque trait
pointillé représente la distance que l’on cherche à calculer entre les points de la courbe et l’origine du repère de coordonnées
(0, 0).

Figure 10.2 – Illustration de la distance à l’origine.

Votre programme générera un fichier [Link] qui contiendra deux colonnes : la première représente les x, la
seconde la distance entre chaque point de la fonction sin(x) à l’origine.

Enfin, pour visualiser votre résultat, ajoutez le code suivant tout à la fin de votre script :

100 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


10.8. Exercices Chapitre 10. Fonctions

1 # Création d'une image pour la visualisation du résultat.


2 import [Link] as plt
3
4 x = []
5 y = []
6 with open("[Link]", "r") as f_in:
7 for line in f_in:
8 coords = [Link]()
9 [Link](float(coords[0]))
10 [Link](float(coords[1]))
11 fig, ax = [Link](figsize=(6, 6))
12 [Link](x, y)
13 ax.set_xlabel("x")
14 ax.set_ylabel("Distance de sin(x) à l'origine")
15 [Link]("[Link]")

Ouvrez l’image [Link].

Remarque
Le module matplotlib sera expliqué en détail dans le chapitre 21 Module matplotlib.

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 101


CHAPITRE 11

Plus sur les chaînes de caractères

11.1 Préambule
Nous avons déjà abordé les chaînes de caractères dans les chapitres 2 Variables et 3 Affichage. Ici nous allons un peu
plus loin, notamment avec les méthodes associées aux chaînes de caractères 1 .

11.2 Chaînes de caractères et listes


Les chaînes de caractères peuvent être considérées comme des listes (de caractères) un peu particulières :
1 >>> animaux = "girafe tigre"
2 >>> animaux
3 'girafe tigre'
4 >>> len(animaux)
5 12
6 >>> animaux[3]
7 'a'

Nous pouvons donc utiliser certaines propriétés des listes comme les tranches :
1 >>> animaux = "girafe tigre"
2 >>> animaux[0:4]
3 'gira'
4 >>> animaux[9:]
5 'gre'
6 >>> animaux[:-2]
7 'girafe tig'
8 >>> animaux[1:-2:2]
9 'iaetg'

Mais a contrario des listes, les chaînes de caractères présentent toutefois une différence notable, ce sont des listes
non modifiables. Une fois une chaîne de caractères définie, vous ne pouvez plus modifier un de ses éléments. Le cas
échéant, Python renvoie un message d’erreur :

1. [Link]

102
11.3. Caractères spéciaux Chapitre 11. Plus sur les chaînes de caractères

1 >>> animaux = "girafe tigre"


2 >>> animaux[4]
3 'f'
4 >>> animaux[4] = "F"
5 Traceback (most recent call last):
6 File "<stdin>", line 1, in <module>
7 TypeError: 'str' object does not support item assignment

Par conséquent, si vous voulez modifier une chaîne de caractères, vous devez en construire une nouvelle. Pour cela,
n’oubliez pas que les opérateurs de concaténation (+) et de duplication (*) (introduits dans le chapitre 2 Variables)
peuvent vous aider. Vous pouvez également générer une liste, qui elle est modifiable, puis revenir à une chaîne de
caractères (voir plus bas).

11.3 Caractères spéciaux


Il existe certains caractères spéciaux comme \n que nous avons déjà vu (pour le retour à la ligne). Le caractère \t
produit une tabulation. Si vous voulez écrire des guillemets simples ou doubles et que ceux-ci ne soient pas confondus
avec les guillemets de déclaration de la chaîne de caractères, vous pouvez utiliser \' ou \" :
1 >>> print("Un backslash n\npuis un backslash t\t puis un guillemet\"")
2 Un backslash n
3 puis un backslash t puis un guillemet"
4 >>> print('J\'affiche un guillemet simple')
5 J'affiche un guillemet simple

Vous pouvez aussi utiliser astucieusement des guillemets doubles ou simples pour déclarer votre chaîne de caractères :
1 >>> print("Un brin d'ADN")
2 Un brin d'ADN
3 >>> print('Python est un "super" langage de programmation')
4 Python est un "super" langage de programmation

Quand on souhaite écrire un texte sur plusieurs lignes, il est très commode d’utiliser les guillemets triples qui conservent
le formatage (notamment les retours à la ligne) :
1 >>> x = """souris
2 ... chat
3 ... abeille"""
4 >>> x
5 'souris\nchat\nabeille'
6 >>> print(x)
7 souris
8 chat
9 abeille

Attention, les caractères spéciaux n’apparaissent intérprétés que lorsqu’ils sont utilisés avec la fonction print(). Par
exemple, le \n n’apparait comme un retour à la ligne que lorsqu’il est dans une chaîne de caractères passée à la fonction
print() :
1 >>> "bla\nbla"
2 'bla\nbla'
3 >>> print("bla\nbla")
4 bla
5 bla

11.4 Préfixe de chaîne de caractères


Nous avons vu au chapitre 3 Affichage la notion de f-string. Il s’agit d’un mécanisme pour formater du texte au sein
d’une chaîne de caractères. Par exemple :
1 >>> var = "f-string"
2 >>> f"voici une belle {var}"
3 'voici une belle f-string'

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 103


Chapitre 11. Plus sur les chaînes de caractères 11.4. Préfixe de chaîne de caractères

Que signifie le f que l’on accole aux guillemets de la chaîne de caractères ? Celui-ci est appelé « préfixe de chaîne de
caractères » ou stringprefix.

Remarque
Un stringprefix modifie la manière dont Python va interpréter ladite string. Celui-ci doit être systématiquement «
collé » à la chaîne de caractères, c’est-à-dire sans espace entre les deux.

Il existe différents stringprefixes en Python, nous vous montrons ici les deux qui nous apparaissent les plus importants.
• Le préfixe r mis pour raw string, qui force la non-interprétation des caractères spéciaux :
1 >>> s = "Voici un retour à la ligne\nEt là une autre ligne"
2 >>> s
3 'Voici un retour à la ligne\nEt là une autre ligne'
4 >>> print(s)
5 Voici un retour à la ligne
6 Et là une autre ligne
7 >>> s = r"Voici un retour à la ligne\nEt là une autre ligne"
8 >>> s
9 'Voici un retour à la ligne\\nEt là une autre ligne'
10 >>> print(s)
11 Voici un retour à la ligne\nEt là une autre ligne

L’ajout du r va forcer Python à ne pas interpréter le \n comme un retour à la ligne, mais comme un backslash littéral
suivi d’un n. Quand on demande à l’interpréteur d’afficher cette chaîne de caractères, celui-ci met deux backslashes
pour signifier qu’il s’agit d’un backslash littéral (le premier échappe le second). Finalement, l’utilisation de la syntaxe
r"Voici un retour à la ligne\nEt là une autre ligne" renvoie une chaîne de caractères normale, puisqu’on
voit ensuite que le r a disparu lorsqu’on demande à Python d’afficher le contenu de la variable s. Comme dans var = 2
+ 2, d’abord Python évalue 2 + 2. Puis ce résultat est affecté à la variable var. Enfin, on notera que seule l’utilisation
du print() mène à l’interprétation des caractères spéciaux comme \n, comme expliqué dans la rubrique précédente.
Les caractères spéciaux non interprétés dans les raw strings sont de manière générale tout ce dont le backslash modifie
la signification, par exemple un \n, un \t, etc.
• Le préfixe f mis pour formatted string, qui met en place l’écriture formatée comme vue au chapitre 3 Affichage :
1 >>> animal = "renard"
2 >>> animal2 = "poulain"
3 >>> s = f"Le {animal} est un animal gentil\nLe {animal2} aussi"
4 >>> s
5 'Le renard est un animal gentil\nLe poulain aussi'
6 >>> print(s)
7 Le renard est un animal gentil
8 Le poulain aussi
9 >>> s = "Le {animal} est un animal gentil\nLe {animal2} aussi"
10 >>> s
11 'Le {animal} est un animal gentil\nLe {animal2} aussi'
12 >>> print(s)
13 Le {animal} est un animal gentil
14 Le {animal2} aussi

La f-string remplace le contenu des variables situées entre les accolades et interprète le \n comme un retour à la
ligne. Pour rappel, consultez le chapitre 3 si vous souhaitez plus de détails sur le fonctionnement des f-strings.

Conseil
Il existe de nombreux autres détails concernant les préfixes qui vont au-delà de ce cours. Pour en savoir plus, vous
pouvez consulter la documentations officielle 2 .

2. [Link]

104 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


11.5. Méthodes associées aux chaînes de caractères Chapitre 11. Plus sur les chaînes de caractères

11.5 Méthodes associées aux chaînes de caractères


Voici quelques méthodes 3 spécifiques aux objets de type str :
1 >>> x = "girafe"
2 >>> [Link]()
3 'GIRAFE'
4 >>> x
5 'girafe'
6 >>> 'TIGRE'.lower()
7 'tigre'

Les méthodes .lower() et .upper() renvoient un texte en minuscule et en majuscule respectivement. On remarque
que l’utilisation de ces méthodes n’altère pas la chaîne de caractères de départ, mais renvoie une chaîne de caractères
transformée.
Pour mettre en majuscule la première lettre seulement, vous pouvez faire :
1 >>> x[0].upper() + x[1:]
2 'Girafe'

ou plus simplement utiliser la méthode adéquate :


1 >>> [Link]()
2 'Girafe'

Il existe une méthode associée aux chaînes de caractères qui est particulièrement pratique, la méthode .split() :
1 >>> animaux = "girafe tigre singe souris"
2 >>> [Link]()
3 ['girafe', 'tigre', 'singe', 'souris']
4 >>> for animal in [Link]():
5 ... print(animal)
6 ...
7 girafe
8 tigre
9 singe
10 souris

La méthode .split() découpe une chaîne de caractères en plusieurs éléments appelés champs, en utilisant comme
séparateur n’importe quelle combinaison « d’espace(s) blanc(s) ».

Définition
Un espace blanc 4 (whitespace en anglais) correspond aux caractères qui sont invisibles à l’œil, mais qui occupent de
l’espace dans un texte. Les espaces blancs les plus classiques sont l’espace, la tabulation et le retour à la ligne.

Il est possible de modifier le séparateur de champs, par exemple :


1 >>> animaux = "girafe:tigre:singe::souris"
2 >>> [Link](":")
3 ['girafe', 'tigre', 'singe', '', 'souris']

Attention, dans cet exemple, le séparateur est un seul caractères « : » (et non pas une combinaison de un ou plusieurs
:) conduisant ainsi à une chaîne vide entre singe et souris.
Il est également intéressant d’indiquer à .split() le nombre de fois qu’on souhaite découper la chaîne de caractères
avec l’argument maxsplit :
1 >>> animaux = "girafe tigre singe souris"
2 >>> [Link](maxsplit=1)
3 ['girafe', 'tigre singe souris']
4 >>> [Link](maxsplit=2)
5 ['girafe', 'tigre', 'singe souris']

3. [Link]
4. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 105


Chapitre 11. Plus sur les chaînes de caractères 11.5. Méthodes associées aux chaînes de caractères

La méthode .find(), quant à elle, recherche une chaîne de caractères passée en argument :
1 >>> animal = "girafe"
2 >>> [Link]("i")
3 1
4 >>> [Link]("afe")
5 3
6 >>> [Link]("z")
7 -1
8 >>> [Link]("tig")
9 -1

Si l’élément recherché est trouvé, alors l’indice du début de l’élément dans la chaîne de caractères est renvoyé. Si
l’élément n’est pas trouvé, alors la valeur -1 est renvoyée.
Si l’élément recherché est trouvé plusieurs fois, seul l’indice de la première occurrence est renvoyé :
1 >>> animaux = "girafe tigre"
2 >>> [Link]("i")
3 1

On trouve aussi la méthode .replace() qui substitue une chaîne de caractères par une autre :
1 >>> animaux = "girafe tigre"
2 >>> [Link]("tigre", "singe")
3 'girafe singe'
4 >>> [Link]("i", "o")
5 'gorafe togre'

La méthode .count() compte le nombre d’occurrences d’une chaîne de caractères passée en argument :
1 >>> animaux = "girafe tigre"
2 >>> [Link]("i")
3 2
4 >>> [Link]("z")
5 0
6 >>> [Link]("tigre")
7 1

La méthode .startswith() vérifie si une chaîne de caractères commence par une autre chaîne de caractères :
1 >>> chaine = "Bonjour monsieur le capitaine !"
2 >>> [Link]("Bonjour")
3 True
4 >>> [Link]("Au revoir")
5 False

Cette méthode est particulièrement utile lorsqu’on lit un fichier et que l’on veut récupérer certaines lignes commençant
par un mot-clé. Par exemple dans un fichier PDB, les lignes contenant les coordonnées des atomes commencent par le
mot-clé ATOM.
Enfin, la méthode .strip() permet de « nettoyer les bords » d’une chaîne de caractères :
1 >>> chaine = " Comment enlever les espaces au début et à la fin ? "
2 >>> [Link]()
3 'Comment enlever les espaces au début et à la fin ?'

La méthode .strip() enlève les espaces situés sur les bords de la chaîne de caractère mais pas ceux situés entre des
caractères visibles. En réalité, cette méthode enlève n’importe quel combinaison « d’espace(s) blanc(s) » sur les bords,
par exemple :
1 >>> chaine = " \tfonctionne avec les tabulations et les retours à la ligne\n"
2 >>> [Link]()
3 'fonctionne avec les tabulations et les retours à la ligne'

Cette méthode est utile pour se débarrasser des retours à la ligne quand on lit un fichier.

106 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


11.6. Extraction de valeurs numériques d’une chaîne de caractères Chapitre 11. Plus sur les chaînes de caractères

11.6 Extraction de valeurs numériques d’une chaîne de caractères


Une tâche courante en Python est de lire une chaîne de caractères (provenant par exemple d’un fichier), d’en extraire
des valeurs pour ensuite les manipuler.
On considère par exemple la chaîne de caractères chaine1 :
1 >>> chaine1 = "3.4 17.2 atom"

On souhaite extraire les valeurs 3.4 et 17.2 pour ensuite les additionner.
D’abord, on découpe la chaîne de caractères avec la méthode .split() :
1 >>> liste1 = [Link]()
2 >>> liste1
3 ['3.4', '17.2', 'atom']
4 >>> nb1, nb2, nom = liste1
5 >>> nb1
6 '3.4'
7 >>> nb2
8 '17.2'

On obtient alors une liste de chaînes de caractères liste1. Avec l’affectation multiple, on récupère les nombres
souhaités dans nb1 et nb2, mais ils sont toujours sous forme de chaîne de caractères. Il faut ensuite les convertir en floats
pour pouvoir les additionner :
1 >>> float(nb1) + float(nb2)
2 20.599999999999998

Remarque
Retenez bien l’utilisation des instructions précédentes pour extraire des valeurs numériques d’une chaîne de caractères.
Elles sont régulièrement employées pour analyser des données extraites d’un fichier.

11.7 Fonction map()

Conseil
Si vous êtes débutant, vous pouvez sauter cette rubrique.

La fonction map() permet d’appliquer une fonction à plusieurs éléments d’un objet itérable. Par exemple, si on a une
chaîne de caractères avec trois entiers séparés par des espaces, on peut extraire et convertir les trois nombres en entier
en une seule ligne. La fonction map() produit un objet de type map qui est itérable et transformable en liste :
1 >>> ligne = "67 946 -45"
2 >>> [Link]()
3 ['67', '946', '-45']
4 >>> map(int, [Link]())
5 <map object at 0x7fa34e573b20>
6 >>> for entier in map(int, [Link]()):
7 ... print(entier)
8 ...
9 67
10 946
11 -45
12 >>> list(map(int, [Link]()))
13 [67, 946, -45]

Remarque

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 107


Chapitre 11. Plus sur les chaînes de caractères 11.8. Test d’appartenance

La fonction map() prend deux arguments. Le second est un objet itérable, souvent une liste comme dans notre
exemple. Le premier argument est le nom d’une fonction qu’on souhaite appliquer à chaque élément de la liste, mais sans
les parenthèses (ici int et non pas int()). Une fonction passée en argument d’une autre fonction est appelée fonction
de rappel 5 ou callback en anglais. Nous reverrons cette notion dans le chapitre 25 Fenêtres graphiques et Tkinter (en
ligne).

La fonction map() est particulièrement utile lorsqu’on lit un fichier de valeurs numériques. Par exemple, si on a un
fichier [Link] contenant trois colonnes de nombres, map() en conjonction avec .split() permet de séparer les trois
nombres puis de les convertir en float en une seule ligne de code :
1 with open("[Link]", "r") as filin:
2 for line in filin:
3 x, y, z = map(float, [Link]())
4 print(x + y + z)

Sans map(), il aurait fallu une ligne pour séparer les données x, y, z = [Link]() et une autre pour les
transformer en float x, y, z = float(x), float(y), float(z).
Enfin, on peut utiliser map() avec ses propres fonctions :
1 >>> def calc_cube(x):
2 ... return x**3
3 ...
4 >>> list(map(calc_cube, [1, 2, 3, 4]))
5 [1, 8, 27, 64]

11.8 Test d’appartenance


L’opérateur in teste si une chaîne de caractères fait partie d’une autre chaîne de caractères :
1 >>> chaine = "Néfertiti"
2 >>> "toto" in chaine
3 False
4 >>> "titi" in chaine
5 True
6 >>> "ti" in chaine
7 True

Notez que la chaîne testée peut-être présente à n’importe quelle position dans l’autre chaîne. Par ailleurs, le test est
vrai si elle est présente une ou plusieurs fois.
La variation avec l’opérateur booléen not permet de vérifier qu’une chaîne n’est pas présente dans une autre chaîne :
1 >>> not "toto" in chaine
2 True
3 >>> not "fer" in chaine
4 False

11.9 Conversion d’une liste de chaînes de caractères en une chaîne de ca-


ractères
On a vu dans le chapitre 2 Variables la conversion d’un type simple (entier, float et chaîne de caractères) en un
autre avec les fonctions int(), float() et str(). La conversion d’une liste de chaînes de caractères en une chaîne de
caractères est moins intuitive. Elle fait appelle à la méthode .join() :

5. [Link]

108 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


11.10. Method chaining Chapitre 11. Plus sur les chaînes de caractères

1 >>> seq = ["A", "T", "G", "A", "T"]


2 >>> seq
3 ['A', 'T', 'G', 'A', 'T']
4 >>> "-".join(seq)
5 'A-T-G-A-T'
6 >>> " ".join(seq)
7 'A T G A T'
8 >>> "".join(seq)
9 'ATGAT'

Les éléments de la liste initiale sont concaténés les uns à la suite des autres et intercalés par un séparateur, qui peut
être n’importe quelle chaîne de caractères. Ici, on a utilisé un tiret, un espace et rien (une chaîne de caractères vide).
Attention, la méthode .join() ne s’applique qu’à une liste de chaînes de caractères :
1 >>> maliste = ["A", 5, "G"]
2 >>> " ".join(maliste)
3 Traceback (most recent call last):
4 File "<stdin>", line 1, in <module>
5 TypeError: sequence item 1: expected str instance, int found

On espère qu’après ce petit tour d’horizon vous serez convaincu de la richesse des méthodes associées aux chaînes de
caractères. Pour avoir une liste exhaustive de l’ensemble des méthodes associées à une variable particulière, vous pouvez
utiliser la fonction dir() :
1 >>> animaux = "girafe tigre"
2 >>> dir(animaux)
3 ['__add__', '__class__', '__contains__', '__delattr__', '__dir__',
4 ...,
5 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition',
6 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip',
7 'swapcase', 'title', 'translate', 'upper', 'zfill']

Pour l’instant, vous pouvez ignorer les méthodes qui commencent et qui se terminent par deux tirets bas (underscores)
__. Nous n’avons pas mis l’ensemble de la sortie de cette commande dir() pour ne pas surcharger le texte, mais n’hésitez
pas à la tester dans l’interpréteur.
Vous pouvez également accéder à l’aide et à la documentation d’une méthode particulière avec help(), par exemple
pour la méthode .split() :
>>> help([Link])
Help on built-in function split:

split(...)
[Link]([sep [,maxsplit]]) -> list of strings

Return a list of the words in the string S, using sep as the


delimiter string. If maxsplit is given, at most maxsplit
splits are done. If sep is not specified or is None, any
whitespace string is a separator.
(END)

Attention à ne pas mettre les parenthèses à la suite du nom de la méthode. L’instruction correcte est help(animaux
.split) et non pas help([Link]()).

11.10 Method chaining


Il existe de nombreuses méthodes pour traiter les chaînes de caractères. Ces méthodes renvoient la plupart du temps
une chaîne de caractères modifiée.
Par exemple, si on souhaite mettre une majuscule à tous les mots d’une chaîne de caractères, puis remplacer un mot
par un autre, puis transformer cette chaîne de caractères en une liste de chaînes de caractères, on peut écrire :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 109


Chapitre 11. Plus sur les chaînes de caractères 11.11. Exercices

1 >>> message = "salut patrick salut pierre"


2 >>> message1 = [Link]()
3 >>> message1
4 'Salut Patrick Salut Pierre'
5 >>> message2 = [Link]("Salut", "Bonjour")
6 >>> message2
7 'Bonjour Patrick Bonjour Pierre'
8 >>> [Link]()
9 ['Bonjour', 'Patrick', 'Bonjour', 'Pierre']

On a créé deux variables intermédiaires message1 et message2 pour stocker les chaînes de caractères modifiées par
les méthodes .title() et .replace().
Il est possible de faire la même chose en une seule ligne, en utilisant le chaînage de méthodes ou method chaining :
1 >>> message = "salut patrick salut pierre"
2 >>> [Link]().replace("Salut", "Bonjour").split()
3 ['Bonjour', 'Patrick', 'Bonjour', 'Pierre']

On évite ainsi de créer des variables intermédiaires.


Le method chaining peut créer des lignes de code très longues. On peut couper une ligne de code en plusieurs lignes
en utilisant le caractère \ en fin de ligne :
1 >>> message = "salut patrick salut pierre"
2 >>> [Link]() \
3 ... .replace("Salut", "Bonjour") \
4 ... .title()
5 'Bonjour Patrick Bonjour Pierre'

On peut aussi utiliser des parenthèses pour couper une ligne de code en plusieurs lignes :
1 >>> message = "salut patrick salut pierre"
2 >>> (message
3 ... .title()
4 ... .replace("Salut", "Bonjour")
5 ... .split()
6 ... )
7 ['Bonjour', 'Patrick', 'Bonjour', 'Pierre']

L’utilisation de parenthèses permet aussi de couper une chaîne de caractères en plusieurs lignes :
1 >>> ma_chaine = (
2 ... "voici une chaine de caractères "
3 ... "très longue "
4 ... "sur plusieurs lignes")
5 >>> ma_chaine
6 'voici une chaine de caractères très longue sur plusieurs lignes'

Nous reverrons le method chaining dans le chapitre 22 Module Pandas.

11.11 Exercices

Conseil
Pour ces exercices, créez des scripts puis exécutez-les dans un shell.

11.11.1 Parcours d’une liste de chaînes de caractères


Soit la liste ['girafe', 'tigre', 'singe', 'souris']. Avec une boucle, affichez chaque élément ainsi que sa
taille (nombre de caractères).

110 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


11.11. Exercices Chapitre 11. Plus sur les chaînes de caractères

11.11.2 Lecture d’une séquence à partir d’un fichier FASTA


Le fichier UBI4_SCerevisiae.fasta 6 contient une séquence d’ADN au format FASTA.
Créez une fonction lit_fasta() qui prend comme argument le nom d’un fichier FASTA sous la forme d’une chaîne
de caractères, lit la séquence dans le fichier FASTA et la renvoie sous la forme d’une chaîne de caractères.
Utilisez ensuite cette fonction pour récupérer la séquence d’ADN dans la variable sequence puis pour afficher les
informations suivantes :
• le nom du fichier FASTA,
• la longueur de la séquence (c’est-à-dire le nombre de bases qu’elle contient),
• un message vérifiant que le nombre de bases est (ou non) un multiple de 3,
• le nombre de codons (on rappelle qu’un codon est un bloc de 3 bases),
• les 10 premières bases,
• les 10 dernières bases.
La sortie produite par le script devrait ressembler à ça :
UBI4_SCerevisiae.fasta
La séquence contient WWW bases
La longueur de la séquence est un multiple de 3 bases
La séquence possède XXX codons
10 premières bases : YYYYYYYYYY
10 dernières bases : ZZZZZZZZZZ

où WWW et XXX sont des entiers et YYYYYYYYYY et ZZZZZZZZZZ sont des bases.

Conseil
Vous trouverez des explications sur le format FASTA et des exemples de code dans l’annexe A Quelques formats de
données en biologie.

11.11.3 Fréquence des bases dans une séquence d’ADN


Soit la séquence d’ADN ATATACGGATCGGCTGTTGCCTGCGTAGTAGCGT. On souhaite calculer la fréquence de chaque
base A, T, C et G dans cette séquence et afficher le résultat à l’écran.
Créez pour cela une fonction calc_composition() à laquelle vous passez en argument votre séquence d’ADN sous
forme d’une chaîne de caractères, et qui renvoie une liste de quatre floats indiquant respectivement la fréquence en bases
A, T, G et C.

11.11.4 Distance de Hamming


La distance de Hamming 7 mesure la différence entre deux séquences de même taille en comptant le nombre de
positions qui, pour chaque séquence, ne correspondent pas au même acide aminé.
Créez la fonction dist_hamming() qui prend en argument deux chaînes de caractères et qui renvoie la distance de
Hamming (sous la forme d’un entier) entre ces deux chaînes de caractères.
Calculez la distance de Hamming entre les séquences : AGWPSGGASAGLAIL et IGWPSAGASAGLWIL
puis entre les séquences : ATTCATACGTTACGATT et ATACTTACGTAACCATT.

11.11.5 Moyenne de notes


Le fichier [Link] 8 contient des noms d’étudiant ainsi que leurs notes dans différentes matières. Chaque donnée
est séparée par une virgule. On trouve dans l’ordre le nom de l’étudiant, la note en géographie, la note en sport, la note
en anglais.
Jason,17,3,1
William,9,18,15
Susan,3,8,10
[...]

6. [Link]
7. [Link]
8. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 111


Chapitre 11. Plus sur les chaînes de caractères 11.11. Exercices

Créez un programme qui lit chaque ligne du fichier et construit une liste de dictionnaire du style [{"nom": "Jason",
"geo": 17, "sport": 3, "anglais": 1}, ...]. Utilisez si possible la fonction map() pour convertir les nombres
lus dans le fichier en entiers. Réalisez ensuite une boucle sur cette liste de dictionnaires, et affichez le nom de l’étudiant,
sa note en sport et sa note en anglais. Affichez ensuite la moyenne des notes de sport et de géographie pour tous les
étudiants.

11.11.6 Conversion des acides aminés du code à trois lettres au code à une lettre
Créez une fonction convert_3_lettres_1_lettre() qui prend en argument une chaîne de caractères avec des
acides aminés en code à trois lettres et renvoie une chaîne de caractères avec les acides aminés en code à une lettre.
Vous pourrez tenter d’utiliser le method chaining dans cette fonction.
Utilisez cette fonction pour convertir la séquence protéique ALA GLY GLU ARG TRP TYR SER GLY ALA TRP.
Rappel de la nomenclature des acides aminés :

Acide aminé Code 3-lettres Code 1-lettre Acide aminé Code 3-lettres Code 1-lettre
Alanine Ala A Leucine Leu L
Arginine Arg R Lysine Lys K
Asparagine Asn N Méthionine Met M
Aspartate Asp D Phénylalanine Phe F
Cystéine Cys C Proline Pro P
Glutamate Glu E Sérine Ser S
Glutamine Gln Q Thréonine Thr T
Glycine Gly G Tryptophane Trp W
Histidine His H Tyrosine Tyr Y
Isoleucine Ile I Valine Val V

11.11.7 Palindrome
Un palindrome est un mot ou une phrase dont l’ordre des lettres reste le même si on le lit de gauche à droite ou de
droite à gauche. Par exemple, « ressasser » et « engage le jeu que je le gagne » sont des palindromes.
Créez la fonction est_palindrome() qui prend en argument une chaîne de caractères et qui renvoie un booléen
(True si l’argument est un palindrome, False si ce n’est pas le cas). Dans le programme principal, affichez xxx est
un palindrome si la fonction est_palindrome() renvoie True sinon xxx n'est pas un palindrome. Pensez à
vous débarrasser au préalable des majuscules, des signes de ponctuations et des espaces.
Testez ensuite si les expressions suivantes sont des palindromes :
• Radar
• Never odd or even
• Karine alla en Iran
• Un roc si biscornu
• Et la marine ira vers Malte
• Deer Madam, Reed
• rotator
• Was it a car or a cat I saw?

Conseil
Pour le nettoyage de la chaîne de caractères (retrait des majuscules, signes de ponctations et espaces), essayer d’utiliser
le method chaining.

11.11.8 Mot composable


Un mot est composable à partir d’une séquence de lettres si la séquence contient toutes les lettres du mot. Chaque
lettre de la séquence ne peut être utilisée qu’une seule fois. Par exemple, « coucou » est composable à partir de «
uocuoceokzefhu ».

112 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


11.11. Exercices Chapitre 11. Plus sur les chaînes de caractères

Créez la fonction est_composable(), qui prend en argument un mot (sous la forme d’une chaîne de caractères) et
une séquence de lettres (aussi comme une chaîne de caractères), et qui renvoie True si le mot est composable à partir
de la séquence, sinon False.
Dans le programme principal, créez une liste de tuples contenant les couples mot / séquence, de la forme [('mot1',
'sequence1'), ('mot2', 'sequence2'), ...]. Utilisez ensuite une boucle sur tous les couples mot / séquence, et
appelez à chaque itération la fonction est_composable(). Affichez enfin Le mot xxx est composable à partir
de yyy si le mot xxx est composable à partir de la séquence de lettres (yyy). Affichez Le mot xxx n'est pas
composable à partir de yyy si ce n’est pas le cas.
Testez cette fonction avec les mots et les séquences suivantes :

Mot Séquence
python aophrtkny
python aeiouyhpq
coucou uocuoceokzezh
fonction nhwfnitvkloco

11.11.9 Alphabet et pangramme


Les codes ASCII des lettres minuscules de l’alphabet vont de 97 (lettre « a ») à 122 (lettre « z »). La fonction chr()
prend en argument un code ASCII sous la forme d’un entier et renvoie le caractère correspondant (sous la forme d’une
chaîne de caractères). Ainsi chr(97) renvoie 'a', chr(98) renvoie 'b' et ainsi de suite.
Créez la fonction get_alphabet() qui utilise une boucle et la fonction chr() et qui renvoie une chaîne de caractères
contenant toutes les lettres de l’alphabet.
Un pangramme 9 est une phrase comportant au moins une fois chaque lettre de l’alphabet. Par exemple, « Portez ce
vieux whisky au juge blond qui fume » est un pangramme.
Créez la fonction est_pangramme() qui utilise la fonction get_alphabet() précédente, qui prend en argument une
chaîne de caractères xxx, et qui renvoie True si la phrase est un pangramme et False sinon.
Le programme affichera finalement xxx est un pangramme ou xxx n'est pas un pangramme. Pensez à vous
débarrasser des majuscules le cas échéant.
Testez ensuite si les expressions suivantes sont des pangrammes :
• Portez ce vieux whisky au juge blond qui fume
• Monsieur Jack vous dactylographiez bien mieux que votre ami Wolf
• Buvez de ce whisky que le patron juge fameux
• Ceci n’est pas un pangramme

11.11.10 Lecture d’une séquence à partir d’un fichier GenBank (exercice +++)
On cherche à récupérer la séquence d’ADN du chromosome I de la levure Saccharomyces cerevisiae contenu dans le
fichier au format GenBank NC_001133.gbk 10 .
Le format GenBank est présenté en détail dans l’annexe A Quelques formats de données en biologie. Pour cet exercice,
vous devez savoir que la séquence démarre après la ligne commençant par le mot ORIGIN et se termine avant la ligne
commençant par les caractères // :
ORIGIN
1 ccacaccaca cccacacacc cacacaccac accacacacc acaccacacc cacacacaca
61 catcctaaca ctaccctaac acagccctaa tctaaccctg gccaacctgt ctctcaactt
[...]
230101 tgttagtgtt agtattaggg tgtggtgtgt gggtgtggtg tgggtgtggg tgtgggtgtg
230161 ggtgtgggtg tgggtgtggt gtggtgtgtg ggtgtggtgt gggtgtggtg tgtgtggg
//

Pour extraire la séquence d’ADN, nous vous proposons d’utiliser un algorithme de « drapeau », c’est-à-dire une variable
qui sera à True lorsqu’on lira les lignes contenant la séquence et à False pour les autres lignes.
9. [Link]
10. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 113


Chapitre 11. Plus sur les chaînes de caractères 11.11. Exercices

Créez une fonction lit_genbank() qui prend comme argument le nom d’un fichier GenBank sous la forme d’une
chaîne de caractères, lit la séquence dans le fichier GenBank et la renvoie sous la forme d’une chaîne de caractères.
Utilisez ensuite cette fonction pour récupérer la séquence d’ADN dans la variable sequence dans le programme
principal. Le script affichera :
NC_001133.gbk
La séquence contient XXX bases
10 premières bases : YYYYYYYYYY
10 dernières bases : ZZZZZZZZZZ

où XXX est un entier et YYYYYYYYYY et ZZZZZZZZZZ sont des bases.


Vous avez toutes les informations pour effectuer cet exercice. Si toutefois vous coincez sur la mise en place du drapeau,
voici l’algorithme en pseudo-code pour vous aider :
drapeau <- Faux
seq <- chaîne de caractères vide
Lire toutes les lignes du fichier:
si la ligne contient //:
drapeau <- Faux
si drapeau est Vrai:
on ajoute à seq la ligne (sans espace, chiffre et retour à la ligne)
si la ligne contient ORIGIN:
drapeau <- Vrai

11.11.11 Affichage des carbones alpha d’une structure de protéine


Téléchargez le fichier [Link] 11 qui correspond à la structure tridimensionnelle de la protéine barstar 12 sur le site
de la Protein Data Bank (PDB).
Créez la fonction trouve_calpha() qui prend en argument le nom d’un fichier PDB (sous la forme d’une chaîne de
caractères), qui sélectionne uniquement les lignes contenant des carbones alpha, qui stocke ces lignes dans une liste et
les renvoie sous la forme d’une liste de chaînes de caractères.
Utilisez la fonction trouve_calpha() pour afficher à l’écran les carbones alpha des deux premiers résidus (acides
aminés).

Conseil
Vous trouverez des explications sur le format PDB et des exemples de code pour lire ce type de fichier en Python
dans l’annexe A Quelques formats de données en biologie.

11.11.12 Calcul des distances entre les carbones alpha consécutifs d’une structure de pro-
téine (exercice +++)
En utilisant la fonction trouve_calpha() précédente, calculez la distance interatomique entre les carbones alpha
des deux premiers résidus (avec deux chiffres après la virgule).
Rappel : la distance euclidienne d entre deux points A et B de coordonnées cartésiennes respectives (xA , yA , zA ) et
(xB , yB , zB ) se calcule comme suit :

d= (xB − xA )2 + (yB − yA )2 + (zB − zA )2

Créez ensuite la fonction calcule_distance() qui prend en argument la liste renvoyée par la fonction trouve_calpha
(), qui calcule les distances interatomiques entre carbones alpha consécutifs et affiche ces distances sous la forme :
numero_calpha_1 numero_calpha_2 distance
Les numéros des carbones alpha seront affichés sur deux caractères. La distance sera affichée avec deux chiffres après
la virgule. Voici un exemple avec les premiers carbones alpha :

11. [Link]
12. [Link]

114 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


11.11. Exercices Chapitre 11. Plus sur les chaînes de caractères

1 2 3.80
2 3 3.80
3 4 3.83
4 5 3.82

Modifiez maintenant la fonction calcule_distance() pour qu’elle affiche à la fin la moyenne des distances.
La distance inter-carbone alpha dans les protéines est très stable et de l’ordre de 3,8 angströms. Observez avec attention
les valeurs que vous avez calculées pour la protéine barstar. Repérez une valeur surprenante. Essayez de l’expliquer.

Conseil
Vous trouverez des explications sur le format PDB et des exemples de code pour lire ce type de fichier en Python
dans l’annexe A Quelques formats de données en biologie.

11.11.13 Compteur de gènes dans un fichier GenBank


Dans cet exercice, on souhaite compter le nombre de gènes du fichier GenBank NC_001133.gbk 13 (chromosome I
de la levure Saccharomyces cerevisiae) et afficher la longueur de chaque gène. Pour cela, il faudra récupérer les lignes
décrivant la position des gènes. Voici par exemple les cinq premières lignes concernées dans le fichier NC_001133.gbk :
gene complement(<1807..>2169)
gene <2480..>2707
gene complement(<7235..>9016)
gene complement(<11565..>11951)
gene <12046..>12426
[...]

Lorsque la ligne contient le mot complement le gène est situé sur le brin complémentaire, sinon il est situé sur le brin
direct. Votre code devra récupérer le premier et le second nombre indiquant respectivement la position du début et de
fin du gène. Attention à bien les convertir en entier afin de pouvoir calculer la longueur du gène. Notez que les caractères
> et < doivent être ignorés, et que les .. servent à séparer la position de début et de fin.
On souhaite obtenir une sortie de la forme :
gène 1 complémentaire -> 362 bases
gène 2 direct -> 227 bases
gène 3 complémentaire -> 1781 bases
[...]
gène 99 direct -> 611 bases
gène 100 direct -> 485 bases
gène 101 direct -> 1403 bases

Conseil
Vous trouverez des explications sur le format GenBank dans l’annexe A Quelques formats de données en biologie.

13. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 115


CHAPITRE 12

Plus sur les listes

Nous avons vu les listes dès le chapitre 4 et les avons largement utilisées depuis le début de ce cours. Dans ce chapitre,
nous allons plus loin avec les méthodes associées aux listes, ainsi que d’autres caractéristiques très puissantes telles que
les tests d’appartenance ou les listes de compréhension.

12.1 Méthodes associées aux listes


Comme pour les chaînes de caractères, les listes possèdent de nombreuses méthodes qui leurs sont propres. On
rappelle qu’une méthode est une fonction qui agit sur l’objet auquel elle est attachée par un point.

12.1.1 .append()
La méthode .append(), que l’on a déjà vu au chapitre 4 Listes, ajoute un élément à la fin d’une liste :
1 >>> liste1 = [1, 2, 3]
2 >>> [Link](5)
3 >>> liste1
4 [1, 2, 3, 5]

qui est équivalent à :


1 >>> liste1 = [1, 2, 3]
2 >>> liste1 = liste1 + [5]
3 >>> liste1
4 [1, 2, 3, 5]

Conseil
Préférez la version avec .append() qui est plus compacte et facile à lire.

12.1.2 .insert()
La méthode .insert() insère un objet dans une liste à un indice déterminé :

116
12.1. Méthodes associées aux listes Chapitre 12. Plus sur les listes

1 >>> liste1 = [1, 2, 3]


2 >>> [Link](2, -15)
3 >>> liste1
4 [1, 2, -15, 3]

12.1.3 del
L’instruction del supprime un élément d’une liste à un indice déterminé :
1 >>> liste1 = [1, 2, 3]
2 >>> del liste1[1]
3 >>> liste1
4 [1, 3]

Remarque
Contrairement aux méthodes associées aux listes présentées dans cette rubrique, del est une instruction générale de
Python, utilisable pour d’autres objets que des listes. Celle-ci ne prend pas de parenthèse.

12.1.4 .remove()
La méthode .remove() supprime un élément d’une liste à partir de sa valeur :
1 >>> liste1 = [1, 2, 3]
2 >>> [Link](3)
3 >>> liste1
4 [1, 2]

S’il y a plusieurs fois la même valeur dans la liste, seule la première est retirée. Il faut appeler la méthode .remove()
autant de fois que nécessaire pour retirer toutes les occurences d’un même élément :
1 >>> liste1 = [1, 2, 3, 4, 3]
2 >>> [Link](3)
3 >>> liste1
4 [1, 2, 4, 3]
5 >>> [Link](3)
6 >>> liste1
7 [1, 2, 4]

12.1.5 .sort()
La méthode .sort() trie les éléments d’une liste du plus petit au plus grand :
1 >>> liste1 = [3, 1, 2]
2 >>> [Link]()
3 >>> liste1
4 [1, 2, 3]

L’argument reverse=True spécifie le tri inverse, c’est-à-dire du plus grand au plus petit élément :
1 >>> liste1 = [3, 1, 2]
2 >>> [Link](reverse=True)
3 >>> liste1
4 [3, 2, 1]

12.1.6 sorted()
La fonction sorted() trie également une liste. Contrairement à la méthode précédente .sort(), cette fonction
renvoie la liste triée et ne modifie pas la liste initiale :
1 >>> liste1 = [3, 1, 2]
2 >>> sorted(liste1)
3 [1, 2, 3]
4 >>> liste1
5 [3, 1, 2]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 117


Chapitre 12. Plus sur les listes 12.1. Méthodes associées aux listes

La fonction sorted() supporte aussi l’argument reverse=True :


1 >>> liste1 = [3, 1, 2]
2 >>> sorted(liste1, reverse=True)
3 [3, 2, 1]
4 >>> liste1
5 [3, 1, 2]

12.1.7 .reverse()
La méthode .reverse() inverse une liste :
1 >>> liste1 = [3, 1, 2]
2 >>> [Link]()
3 >>> liste1
4 [2, 1, 3]

12.1.8 .count()
La méthode .count() compte le nombre d’éléments (passés en argument) dans une liste :
1 >>> liste1 = [1, 2, 4, 3, 1, 1]
2 >>> [Link](1)
3 3
4 >>> [Link](4)
5 1
6 >>> [Link](23)
7 0

12.1.9 Particularités des méthodes associées aux listes


De nombreuses méthodes mentionnées précédemment (.append(), .sort(), etc.) modifient la liste, mais ne ren-
voient pas d’objet récupérable dans une variable. Il s’agit d’un exemple d’utilisation de méthode (donc de fonction
particulière) qui fait une action, mais qui ne renvoie rien. Pensez-y dans vos utilisations futures des listes. Ainsi, même si
l’instruction var = [Link]() est une instruction Python valide, var ne contiendra que None c’est-à-dire un
objet vide en Python, préférez-lui directement l’instruction [Link]() :
1 >>> liste1 = [1, 2, 3]
2 >>> var = [Link]()
3 >>> var
4 >>> print(var)
5 None
6 >>> liste1
7 [3, 2, 1]
8 >>> liste2 = [5, 6, 7]
9 >>> [Link]()
10 >>> liste2
11 [7, 6, 5]

Remarque
Pour exprimer la même idée, la documentation parle de modification de la liste « sur place » (in place en anglais) :
1 >>> liste1 = [1, 2, 3]
2 >>> help([Link])
3 Help on built-in function reverse:
4
5 reverse() method of [Link] instance
6 Reverse *IN PLACE*.

Cela signifie que la liste est modifiée « sur place », c’est-à-dire dans la méthode au moment où elle s’exécute. La liste
étant modifiée « en dur » dans la méthode, cette dernière ne renvoie donc rien. L’explication du mécanisme sous-jacent
vous sera donnée dans la rubrique 13.4 Portée des listes du chapitre 13 Plus sur les fonctions.

Par ailleurs, certaines méthodes ou instructions des listes décalent les indices d’une liste (par exemple .insert(),

118 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


12.2. Construction d’une liste par itération Chapitre 12. Plus sur les listes

del, etc.).
Enfin, pour obtenir une liste exhaustive des méthodes disponibles pour les listes, utilisez la fonction dir(liste1)
(liste1 étant une liste).

12.2 Construction d’une liste par itération


La méthode .append() est très pratique car on peut l’utiliser pour construire une liste au fur et à mesure des
itérations d’une boucle.
Pour cela, il est commode de définir préalablement une liste vide avec l’instruction liste1 = []. Voici un exemple
où une chaîne de caractères est convertie en liste :
1 >>> seq = "CAAAGGTAACGC"
2 >>> seq_list = []
3 >>> seq_list
4 []
5 >>> for base in seq:
6 ... seq_list.append(base)
7 ...
8 >>> seq_list
9 ['C', 'A', 'A', 'A', 'G', 'G', 'T', 'A', 'A', 'C', 'G', 'C']

Remarquez que dans cet exemple, vous pouvez aussi utiliser directement la fonction list() qui prend n’importe quel
objet séquentiel (liste, chaîne de caractères, etc.) et qui renvoie une liste :
1 >>> seq = "CAAAGGTAACGC"
2 >>> list(seq)
3 ['C', 'A', 'A', 'A', 'G', 'G', 'T', 'A', 'A', 'C', 'G', 'C']

Cette méthode est certes plus simple, mais il arrive parfois qu’on doive utiliser des boucles tout de même, comme
lorsqu’on lit un fichier. Nous vous rappellons que l’instruction list(seq) convertit un objet de type chaîne de caractères
en un objet de type liste (il s’agit donc d’une opération de casting). De même que list(range(10)) convertit un objet
de type range en un objet de type list.

12.3 Test d’appartenance


L’opérateur in teste si un élément fait partie d’une liste :
1 liste1 = [1, 3, 5, 7, 9]
2 >>> 3 in liste1
3 True
4 >>> 4 in liste1
5 False
6 >>> 3 not in liste1
7 False
8 >>> 4 not in liste1
9 True

La variation avec not permet, a contrario, de vérifier qu’un élément n’est pas dans une liste.

12.4 Fonction zip()

Conseil
Si vous êtes débutant, vous pouvez sauter cette rubrique.

La fonction zip() de Python permet d’itérer sur plusieurs listes en parallèle :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 119


Chapitre 12. Plus sur les listes 12.4. Fonction zip()

1 >>> animaux = ["poulain", "renard", "python"]


2 >>> couleurs = ["alezan", "roux", "vert"]
3 >>> zip(animaux, couleurs)
4 <zip object at 0x7f6cf954a480>
5 >>> type(zip(animaux, couleurs))
6 <class 'zip'>
7 >>> for element in zip(animaux, couleurs):
8 ... print(element)
9 ...
10 ('poulain', 'alezan')
11 ('renard', 'roux')
12 ('python', 'vert')
13 >>> for animal, couleur in zip(animaux, couleurs):
14 ... print(f"le {animal} est {couleur}")
15 ...
16 le poulain est alezan
17 le renard est roux
18 le python est vert

Lignes 3 et 6. On passe en argument deux listes à zip() qui génère un nouvel objet de type zip. Comme pour les
objets de type map vu au chapitre 11 Plus sur les chaînes de caractères, les objets zip sont itérables.
Lignes 7 à 12. Lorsqu’on itère sur un objet zip, la variable d’itération est un tuple. À la première itération, on a un
tuple avec le premier élément de chaque liste utilisée pour générer l’objet zip, à la deuxième itération, ce sera le deuxième
élément, et ainsi de suite.
Lignes 13 à 18. Avec l’affectation multiple, on peut affecter à la volée les éléments à des variables différentes, comme
on l’a fait avec la fonction enumerate() (chapitre 5 Boucles) et la méthode .items() des dictionnaires (chapitre 8
Dictionnaires et tuples).
Un objet zip est aussi utile pour générer facilement une liste de tuples.
1 >>> list(zip(animaux, couleurs))
2 [('poulain', 'alezan'), ('renard', 'roux'), ('python', 'vert')]

Si une des listes passée en argument n’a pas la même longueur, l’objet zip s’arrête sur la liste la plus courte :
1 >>> animaux = ["poulain", "renard", "python", "orque"]
2 >>> couleurs = ["alezan", "roux", "vert"]
3 >>> list(zip(animaux, couleurs))
4 [('poulain', 'alezan'), ('renard', 'roux'), ('python', 'vert')]

On peut empêcher ce comportement avec l’argument par mot-clé strict, qui renvoie une erreur si les listes n’ont
pas la même longueur :
1 >>> list(zip(animaux, couleurs, strict=True))
2 Traceback (most recent call last):
3 File "<stdin>", line 1, in <module>
4 ValueError: zip() argument 2 is shorter than argument 1

Enfin, il est possible de créer des objets zip avec autant de listes que l’on veut :
1 >>> animaux = ["poulain", "renard", "python"]
2 >>> couleurs = ["alezan", "roux", "vert"]
3 >>> numero = [1, 2, 3]
4 >>> list(zip(numero, animaux, couleurs))
5 [(1, 'poulain', 'alezan'), (2, 'renard', 'roux'), (3, 'python', 'vert')]

Remarque
La fonction zip() fonctionne sur n’importe quel objet itérable : listes, tuples, dictionnaires, objets range, etc.

Conseil
Pour les débutants, vous pouvez sauter cette remarque.

120 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


12.5. Copie de listes Chapitre 12. Plus sur les listes

Un objet zip() comme présenté plus haut est ce qu’on appelle un itérateur. Cela implique un mode de fonctionnement
particulier, notamment le fait qu’on ne peut l’utiliser qu’une fois lorsqu’on l’a créé. Vous trouverez plus d’explications sur
la définition et le fonctionnement d’un itérateur dans le chapitre 26 Remarques complémentaires.

12.5 Copie de listes


Il est très important de savoir que l’affectation d’une liste (à partir d’une liste préexistante) crée en réalité une
référence et non une copie :
1 >>> liste1 = [1, 2, 3]
2 >>> liste2 = liste1
3 >>> liste2
4 [1, 2, 3]
5 >>> liste1[1] = -15
6 >>> liste1
7 [1, -15, 3]
8 >>> liste2
9 [1, -15, 3]

Vous voyez que la modification de liste1 modifie liste2 aussi ! Pour comprendre ce qu’il se passe, nous allons de
nouveau utiliser le site Python Tutor avec cet exemple (Figure 12.1) :

Figure 12.1 – Copie de liste.

Techniquement, Python utilise des pointeurs (comme dans le langage de programmation C) vers les mêmes objets.
Python Tutor l’illustre avec des flèches qui partent des variables liste1 et liste2 et qui pointent vers la même liste.
Donc, si on modifie la liste liste1, la liste liste2 est modifiée de la même manière. Rappelez-vous de ceci dans vos
futurs programmes, car cela pourrait avoir des effets désastreux !
Pour éviter ce problème, il va falloir créer une copie explicite de la liste initiale. Observez cet exemple :
1 >>> liste1 = [1, 2, 3]
2 >>> liste2 = liste1[:]
3 >>> liste1[1] = -15
4 >>> liste2
5 [1, 2, 3]

L’instruction liste1[:] a créé une copie « à la volée » de la liste liste1. Vous pouvez utiliser aussi la fonction
list(), qui renvoie explicitement une liste :
1 >>> liste1 = [1, 2, 3]
2 >>> liste2 = list(liste1)
3 >>> liste1[1] = -15
4 >>> liste2
5 [1, 2, 3]

Si on regarde à nouveau dans Python Tutor (Figure 12.2), on voit clairement que l’utilisation d’une tranche [:] ou
de la fonction list() crée des copies explicites. Chaque flèche pointe vers une liste différente, indépendante des autres.

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 121


Chapitre 12. Plus sur les listes 12.6. Initialisation d’une liste de listes

Figure 12.2 – Copie de liste avec une tranche [:] et la fonction list().

Attention, les deux astuces précédentes ne fonctionnent que pour les listes à une dimension, autrement dit les listes
qui ne contiennent pas elles-mêmes d’autres listes. Voyez par exemple :
1 >>> liste1 = [[1, 2], [3, 4]]
2 >>> liste1
3 [[1, 2], [3, 4]]
4 >>> liste2 = liste1[:]
5 >>> liste1[1][1] = 55
6 >>> liste1
7 [[1, 2], [3, 55]]
8 >>> liste2
9 [[1, 2], [3, 55]]

et
1 >>> liste2 = list(liste1)
2 >>> liste1[1][1] = 77
3 >>> liste1
4 [[1, 2], [3, 77]]
5 >>> liste2
6 [[1, 2], [3, 77]]

La méthode de copie qui fonctionne à tous les coups consiste à appeler la fonction deepcopy() du module copy :
1 >>> import copy
2 >>> liste1 = [[1, 2], [3, 4]]
3 >>> liste1
4 [[1, 2], [3, 4]]
5 >>> liste2 = [Link](liste1)
6 >>> liste1[1][1] = 99
7 >>> liste1
8 [[1, 2], [3, 99]]
9 >>> liste2
10 [[1, 2], [3, 4]]

12.6 Initialisation d’une liste de listes


Un dernier écueil que vous pourrez rencontrer concerne l’initialisation d’une liste de listes avec l’opérateur *. Imaginons
que l’on souhaite représenter un tableau de nombre et l’initialiser avec des 0. Nous pourrions être tentés d’utiliser la
duplication de listes :
1 >>> liste1 = [[0, 0, 0]] * 5
2 >>> liste1
3 [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

Le problème est que si on modifie un élément d’une des sous-listes :

122 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


12.7. Liste de compréhension Chapitre 12. Plus sur les listes

1 >>> liste1[2][0] = -12


2 >>> liste1
3 [[-12, 0, 0], [-12, 0, 0], [-12, 0, 0], [-12, 0, 0], [-12, 0, 0]]

Vous constatez qu’il est modifié dans chaque sous-liste ! À l’aide de Python Tutor on voit que Python crée une
référence vers la même sous-liste (Figure 12.3) :

Figure 12.3 – Initialisation d’une liste de listes avec l’opérateur de duplication.

Comme disent les auteurs dans la documentation officielle 1 : Note that items in the sequence are not copied ; they
are referenced multiple times. This often haunts new Python programmers. Pour éviter le problème, on peut utiliser une
boucle :
1 >>> liste1 = []
2 >>> for i in range(5):
3 ... [Link]([0, 0, 0])
4 ...
5 >>> liste1
6 [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
7 >>> liste1[2][0] = -12
8 >>> liste1
9 [[0, 0, 0], [0, 0, 0], [-12, 0, 0], [0, 0, 0], [0, 0, 0]]

On verra dans la rubrique suivante une manière très compacte de faire cela avec les listes de compréhension.

Attention
Même si une liste de listes peut représenter un tableau de nombres, il ne faut pas la voir comme un objet mathématique
de type matrice 2 . En effet, le concept de lignes et colonnes n’est pas défini clairement, on ne peut pas faire d’opérations
matricielles simplement, etc. On verra dans le chapitre 20 Module Numpy qu’il existe des objets appelés arrays qui sont
faits pour ça.

12.7 Liste de compréhension

Conseil
Si vous êtes débutant, vous pouvez sauter cette rubrique.

En Python, la notion de liste de compréhension (ou compréhension de listes) représente une manière originale et très
puissante de générer des listes. La syntaxe de base consiste au moins en une boucle for au sein de crochets précédés
d’une variable (qui peut être la variable d’itération ou pas) :

1. [Link]
2. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 123


Chapitre 12. Plus sur les listes 12.7. Liste de compréhension

1 >>> [i for i in range(10)]


2 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3 >>> [2 for i in range(10)]
4 [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]

Pour plus de détails, consultez à ce sujet le site de Python 3 et celui de Wikipédia 4 .


Voici quelques exemples illustrant la puissance des listes de compréhension.

12.7.1 Initialisation d’une liste de listes


Une liste de compréhension permet l’initialisation d’une liste de listes en une ligne sans avoir l’inconvénient de faire
une référence vers la même sous-liste (voir rubrique précédente) :
1 >>> liste1 = [[0, 0, 0] for i in range(5)]
2 >>> liste1
3 [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
4 >>> liste1[2][0] = -12
5 >>> liste1
6 [[0, 0, 0], [0, 0, 0], [-12, 0, 0], [0, 0, 0], [0, 0, 0]]

12.7.2 Nombres pairs compris entre 0 et 30

1 >>> print([i for i in range(31) if i % 2 == 0])


2 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]

12.7.3 Jeu sur la casse des mots d’une phrase

1 >>> message = "C'est sympa la BioInfo"


2 >>> msg_lst = [Link]()
3 >>> print([[[Link](), len(m)] for m in msg_lst])
4 [["C'EST", 5], ['SYMPA', 5], ['LA', 2], ['BIOINFO', 7]]

12.7.4 Formatage d’une séquence avec 60 caractères par ligne


Exemple d’une séquence constituée de 150 alanines :
1 # Exemple d'une séquence de 150 alanines.
2 >>> seq = "A" * 150
3 >>> width = 60
4 >>> seq_split = [seq[i:i+width] for i in range(0, len(seq), width)]
5 >>> print("\n".join(seq_split))
6 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
7 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
8 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

12.7.5 Formatage FASTA d’une séquence


Exemple d’une séquence constituée de 150 alanines :
1 >>> com = "Séquence de 150 alanines"
2 >>> seq = "A" * 150
3 >>> width = 60
4 >>> seq_split = [seq[i:i+width] for i in range(0, len(seq), width)]
5 >>> print(">"+com+"\n"+"\n".join(seq_split))
6 >séquence de 150 alanines
7 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
8 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
9 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

3. [Link]
4. [Link]

124 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


12.8. Tris puissants de listes Chapitre 12. Plus sur les listes

12.7.6 Sélection des carbones alpha dans un fichier PDB


Exemple avec la structure de la barstar 5 :
1 >>> with open("[Link]", "r") as f_pdb:
2 ... CA_lines = [
3 ... line for line in f_pdb
4 ... if [Link]("ATOM") and line[12:16].strip() == "CA"
5 ... ]
6 ...
7 >>> print(len(CA_lines))
8 89

Conseil
Pour plus de lisiblité, il est possible de répartir la liste de compréhension sur plusieurs lignes.

12.7.7 Portée des variables dans une liste de compréhension


Contrairement à une boucle for, la variable d’itération d’une liste de compréhension n’est pas accessible en dehors
de la liste de compréhension elle-même. Par exemple :
1 >>> liste_a = []
2 >>> for idx_a in range(10):
3 ... liste_a.append(idx_a)
4 ...
5 >>> print(liste_a)
6 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
7 >>> print(idx_a)
8 9
9 >>>
10 >>> liste_b = [idx_b for idx_b in range(10)]
11 >>> print(liste_b)
12 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
13 >>> print(idx_b)
14 Traceback (most recent call last):
15 File "<stdin>", line 1, in <module>
16 NameError: name 'idx_b' is not defined. Did you mean: 'idx_a'?

La variable d’itération idx_a reste disponible en dehors de la boucle for. Par contre, la variable d’itération idx_b
n’est pas disponible en dehors de la liste de compréhension, car elle est créée « à la volée » par Python puis éliminée une
fois l’instruction exécutée.

12.8 Tris puissants de listes

Conseil
Si vous êtes débutant, vous pouvez sauter cette rubrique.

Un peu plus haut nous avons évoqué la méthode .sort() qui trie une liste sur place, ainsi que la fonction sorted()
qui renvoie une nouvelle liste triée. Nous avons également vu qu’elles supportaient l’argument par mot-clé reverse pour
trier dans le sens inverse (décroissant ou anti-ASCII). Il existe un autre argument par mot-clé nommé key permettant un
tri avec des règles alternatives que nous pouvons customiser. On doit passer à key une fonction callback (nous avions
déjà croisé cette notion avec la fonction map() dans le chapitre 11 Plus sur les chaînes de caractères, pour une définition
voir le chapitre 25 Fenêtres graphiques et Tkinter (en ligne)), c’est-à-dire, un nom de fonction sans les parenthèses. Par
exemple, si on passe la callback len comme ça :

5. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 125


Chapitre 12. Plus sur les listes 12.9. Exercices

1 >>> mots = ["babar", "bar", "ba", "bababar"]


2 >>> sorted(mots, key=len)
3 ['ba', 'bar', 'babar', 'bababar']

Python trie la liste mots en considérant la longeur de chaque élément, donc ici le nombre de lettres de chaque chaîne
de caractères. Si plusieurs mots ont la même longueur (bar et bam dans l’exemple suivant), sorted() les laisse dans
l’ordre de la liste initiale.
1 >>> mots = ["bar", "babar", "bam", "ba", "bababar"]
2 >>> sorted(mots, key=len)
3 ['ba', 'bar', 'bam', 'babar', 'bababar']

Là où key va se révéler puissant est quand nous allons lui passer une fonction « maison ». Voici une exemple :
1 >>> def compte_b(chaine):
2 ... return [Link]("b")
3 ...
4 >>> compte_b("babar")
5 2
6 >>> mots = ["bar", "babar", "bam", "ba", "bababar"]
7 >>> sorted(mots, key=compte_b)
8 ['bar', 'bam', 'ba', 'babar', 'bababar']

• Lignes 1 à 5. Comme son nom l’indique, la fonction compte_b() compte les lettres b dans une chaîne de
caractères.
• Lignes 7 et 8. En donnant compte_b (notez l’absence de parenthèses) à l’argument key, Python trie en fonction
du nombre de lettres b dans chaque mot ! Comme pour len, si plusieurs mots ont un nombre de lettres b identiques,
il conserve l’ordre de la liste initiale.

Remarque
L’argument key fonctionne de la même manière entre sorted() et la méthode .sort() qui trie sur place. Cet
argument existe aussi avec les fonctions min() et max(). Par exemple :
1 >>> mots = ["bar", "babar", "bam", "ba", "bababar"]
2 >>> min(mots, key=len)
3 'ba'
4 >>> max(mots, key=len)
5 'bababar'

Python renverra le premier élément avec min() ou le dernier élément avec max() après un tri sur la longueur de
chaque mot.

Pour aller plus loin


En Python, trier avec une fonction maison passée à l’argument key se fait plutôt avec ce qu’on appelle une fonction
lambda. Il s’agit d’une « petite » fonction que l’on écrit sur une ligne. Si vous voulez en savoir plus, vous pouvez consulter
le chapitre 26 Remarques complémentaires.

12.9 Exercices

Conseil
Pour ces exercices, créez des scripts puis exécutez-les dans un shell.

126 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


12.9. Exercices Chapitre 12. Plus sur les listes

12.9.1 Tri de liste


Soit la liste de nombres [8, 3, 12.5, 45, 25.5, 52, 1]. Triez les nombres de cette liste par ordre croissant,
sans utiliser la fonction sort(). Les fonctions et méthodes min(), .append() et .remove() vous seront utiles.

12.9.2 Séquence d’ADN aléatoire


Créez une fonction seq_alea() qui prend comme argument un entier positif taille représentant le nombre de bases
de la séquence et qui renvoie une séquence d’ADN aléatoire sous forme d’une chaîne de caractères. Utilisez la fonction
[Link]() présentée dans le chapitre 9 Modules.
Utilisez la fonction seq_alea() pour générer aléatoirement une séquence d’ADN de 15 bases.

12.9.3 Séquence d’ADN complémentaire inverse


Créez une fonction gen_comp_inv() qui prend comme argument une séquence d’ADN sous la forme d’une chaîne de
caractères, qui renvoie la séquence complémentaire inverse sous la forme d’une autre chaîne de caractères et qui utilise
des méthodes associées aux listes. Dans cette fonction, utilisez un dictionnaire {"A": "T", "T": "A", "G": "C",
"C": "G"} donnant la correspondance entre nucléotides des brins direct et complémentaire.
Utilisez cette fonction pour transformer la séquence d’ADN TCTGTTAACCATCCACTTCG en sa séquence complémentaire
inverse.
Rappel : la séquence complémentaire inverse doit être « inversée ». Par exemple, la séquence complémentaire inverse
de la séquence ATCG est CGAT.

12.9.4 Doublons
Soit la liste de nombres liste1 = [5, 1, 1, 2, 5, 6, 3, 4, 4, 4, 2]. À partir de liste1, créez une nouvelle
liste sans les doublons, triez-la et affichez-la.

12.9.5 Séquence d’ADN aléatoire 2


Créez une fonction seq_alea_2() qui prend comme argument un entier et quatre floats, représentant respective-
ment la longueur de la séquence et les pourcentages de chacune des quatre bases A, T, G et C. La fonction générera
aléatoirement une séquence d’ADN qui prend en compte les contraintes fournies en arguments et renverra la séquence
sous forme d’une chaîne de caractères.
Utilisez cette fonction pour générer aléatoirement une séquence d’ADN de 50 bases contenant 10 % de A, 30 % de
T, 50 % de G et 10 % de C.

Conseil
Utilisez la fonction [Link]() avec les paramètres k et weights. Le paramètre k spécifie le nombre de
tirages aléatoires à réaliser et le paramètre weights indique les probabilités de tirage.
Par exemple, pour réaliser 10 tirages aléatoires entre les lettres A et B avec 80% de A et 20% de B, on utilise la
fonction [Link]() de la manière suivante :
1 >>> import random
2 >>> [Link]("AB", k=10, weights=[80, 20])
3 ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'A', 'B']

N’hésitez pas à consulter la documentation 6 de la fonction [Link]() pour plus de détails.

12.9.6 Le nombre mystère


Trouvez le nombre mystère qui répond aux conditions suivantes :
• Il est composé de trois chiffres.
• Il est strictement inférieur à 300.
• Il est pair.
• Deux de ses chiffres sont identiques.
6. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 127


Chapitre 12. Plus sur les listes 12.9. Exercices

• La somme de ses chiffres est égale à 7.


On vous propose d’employer une méthode dite « brute force », c’est-à-dire d’utiliser une boucle et à chaque itération
de tester les différentes conditions.

12.9.7 Codes une et trois lettres des acides aminés


On donne les deux listes suivantes décrivant quelques acides aminés en code une et trois lettres :
1 code_1_lettre = ["A", "V", "L", "M", "P"]
2 code_3_lettres = ["Ala", "Val", "Leu", "Met", "Pro"]

Avec la fonction zip() et une boucle, générez la sortie suivante :


L'acide aminé se note A ou Ala
L'acide aminé se note V ou Val
L'acide aminé se note L ou Leu
L'acide aminé se note M ou Met
L'acide aminé se note P ou Pro

12.9.8 Triangle de Pascal (exercice +++)


Voici le début du triangle de Pascal :
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
[...]

Déduisez comment une ligne est construite à partir de la précédente. Par exemple, à partir de la ligne 2 (1 1),
construisez la ligne suivante (ligne 3 : 1 2 1) et ainsi de suite.
Implémentez cette construction en Python. Généralisez à l’aide d’une boucle.
Écrivez dans un fichier [Link] les 10 premières lignes du triangle de Pascal.

128 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


CHAPITRE 13

Plus sur les fonctions

Avant d’aborder ce chapitre, nous vous conseillons de relire le chapitre 10 Fonctions et de bien en assimiler toutes les
notions (et aussi d’en faire les exercices). Nous avons vu dans ce chapitre 10 le concept incontournable que représentent
les fonctions. Nous avons également introduit la notion de variables locales et globales.
Dans ce chapitre, nous allons aller un peu plus loin sur la visibilité de ces variables dans et hors des fonctions, et
aussi voir ce qui se passe lorsque ces variables sont des listes. Attention, la plupart des lignes de code ci-dessous sont
données à titre d’exemple pour bien comprendre ce qui se passe, mais nombre d’entre elles sont des aberrations en terme
de programmation. Nous ferons un récapitulatif des bonnes pratiques à la fin du chapitre. Enfin, nous vous conseillons
de tester tous les exemples ci-dessous avec le site Python Tutor 1 afin de suivre l’état des variables lors de l’exécution des
exemples.

13.1 Appel d’une fonction dans une fonction


Dans le chapitre 10, nous avons vu des fonctions qui étaient appelées depuis le programme principal. Il est en fait
possible d’appeler une fonction depuis une autre fonction. Et plus généralement, on peut appeler une fonction de n’importe
où à partir du moment où elle est visible par Python (c’est-à-dire chargée dans la mémoire). Observez cet exemple :
1 # Définition des fonctions.
2 def est_pair(x):
3 if x % 2 == 0:
4 return True
5 else:
6 return False
7
8 def calc_somme_nb_pairs(debut, fin):
9 somme = 0
10 for nombre in range(debut, fin+1):
11 if est_pair(nombre):
12 somme += nombre
13 return somme
14
15 # Programme principal.
16 somme = calc_somme_nb_pairs(1, 5)
17 print(f"La somme des nombres pairs de 1 à 5 est {somme}")

Nous appelons la fonction calc_somme_nb_pairs() depuis le programme principal, puis à l’intérieur de celle-ci nous
1. [Link]

129
Chapitre 13. Plus sur les fonctions 13.2. Fonctions récursives

appelons l’autre fonction est_pair(). Regardons ce que Python Tutor nous montre lorsque la fonction calc_somme_nb_pairs
() est exécutée dans la Figure 13.1.

Figure 13.1 – Appel d’une fonction dans une fonction.

L’espace mémoire alloué à est_pair() est grisé, indiquant que cette fonction est en cours d’exécution. La fonction
appelante calc_somme_nb_pairs() est toujours là (sur un fond blanc) car son exécution n’est pas terminée. Elle est
en quelque sorte figée dans le même état qu’avant l’appel de est_pair(), et on pourra ainsi noter que ses variables
locales (debut, fin) sont toujours là. De manière générale, les variables locales d’une fonction ne seront détruites que
lorsque l’exécution de celle-ci sera terminée. Dans notre exemple, les variables locales de calc_somme_nb_pairs() ne
seront détruites que lorsque la boucle sera terminée et que la variable somme sera retournée au programme principal.
Enfin, notez bien que la fonction calc_somme_nb_pairs() appelle la fonction est_pair() à chaque itération de la
boucle.
Ainsi, le programmeur est libre de faire tous les appels qu’il souhaite. Une fonction peut appeler une autre fonction,
cette dernière peut appeler une autre fonction et ainsi de suite (et autant de fois qu’on le veut). Une fonction peut même
s’appeler elle-même, cela s’appelle une fonction récursive (voir la rubrique suivante). Attention toutefois à retrouver vos
petits si vous vous perdez dans les appels successifs !

Conseil
Dans la fonction est_pair() on teste si le nombre est pair et on renvoie True, sinon on renvoie False. Cette
fonction pourrait être écrite de manière plus compacte :
1 def est_pair(x):
2 return x % 2

Comme l’expression x % 2 renvoie un booléen directement, elle revient au même que le if / else ci-dessus. C’est
bien sûr cette dernière notation plus compacte que nous vous recommandons.

13.2 Fonctions récursives

Conseil

130 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


13.2. Fonctions récursives Chapitre 13. Plus sur les fonctions

Si vous êtes débutant, vous pouvez sauter cette rubrique.

Une fonction récursive est une fonction qui s’appelle elle-même. Les fonctions récursives permettent d’obtenir une
efficacité redoutable dans la résolution de certains algorithmes, comme le tri rapide 2 (en anglais, quicksort).
Oublions la recherche d’efficacité pour l’instant et concentrons-nous sur l’exemple de la fonction mathématique
factorielle. Nous vous rappelons que la factorielle s’écrit avec un ! et se définit de la manière suivante :

3! = 3 × 2 × 1 = 6
4! = 4 × 3 × 2 × 1 = 30
...
n! = n × n − 1 × . . . × 2 × 1

Voici le code Python avec une fonction récursive :


1 def calc_factorielle(nb):
2 if nb == 1:
3 return 1
4 else:
5 return nb * calc_factorielle(nb - 1)
6
7 # Programme principal.
8 print(calc_factorielle(4))

Pas si facile à comprendre, n’est-ce pas ? À nouveau, aidons nous de Python Tutor pour visualiser ce qui se passe
dans la figure 13.2 (nous vous conseillons bien sûr de tester vous-même cet exemple) :

Figure 13.2 – Fonction récursive : factorielle.

Ligne 8, on appelle la fonction calc_factorielle() en passant comme argument l’entier 4. Dans la fonction, la
variable locale qui récupère cet argument est nb. Au sein de la fonction, celle-ci se rappelle elle-même (ligne 5), mais cette
fois-ci en passant la valeur 3. Au prochain appel, ce sera avec la valeur 2, puis finalement 1. Dans ce dernier cas, le test
2. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 131


Chapitre 13. Plus sur les fonctions 13.3. Portée des variables

if nb == 1: est vrai et l’instruction return 1 sera exécutée. À ce moment précis de l’exécution, les appels successifs
forment une sorte de pile (voir la figure 13.2). La valeur 1 sera ainsi renvoyée au niveau de l’appel précédent, puis le
résultat 2 × 1 = 2 (où 2 correspond à nb et 1 provient de calc_factorielle(nb - 1), soit 1) va être renvoyé à l’appel
précédent, puis 3 × 2 = 6 (où 3 correspond à nb et 2 provient de calc_factorielle(nb - 1), soit 2) va être renvoyé
à l’appel précédent, pour finir par 4 × 6 = 24 (où 4 correspond à nb et 6 provient de calc_factorielle(nb - 1), soit
6), soit la valeur de 4!. Les appels successifs vont donc se « dépiler » et nous reviendrons dans le programme principal.
Même si les fonctions récursives peuvent être ardues à comprendre, notre propos est ici de vous illustrer qu’une
fonction qui en appelle une autre (ici il s’agit d’elle-même) reste « figée » dans le même état, jusqu’à ce que la fonction
appelée lui renvoie une valeur.

13.3 Portée des variables


Il est très important lorsque l’on manipule des fonctions de connaître la portée des variables (scope en anglais),
c’est-à-dire savoir là où elles sont visibles. On a vu que les variables créées au sein d’une fonction ne sont pas visibles à
l’extérieur de celle-ci car elles étaient locales à la fonction. Observez le code suivant :
1 >>> def ma_fonction():
2 ... x = 2
3 ... print(f"x vaut {x} dans la fonction")
4 ...
5 >>> ma_fonction()
6 x vaut 2 dans la fonction
7 >>> print(x)
8 Traceback (most recent call last):
9 File "<stdin>", line 1, in <module>
10 NameError: name 'x' is not defined

Lorsque Python exécute le code de la fonction, il connaît le contenu de la variable x. Par contre, de retour dans le
module principal (dans ce cas, il s’agit de l’interpréteur Python), il ne la connaît plus, d’où le message d’erreur.
De même, une variable passée en argument est considérée comme locale lorsqu’on arrive dans la fonction :
1 >>> def ma_fonction(x):
2 ... print(f"x vaut {x} dans la fonction")
3 ...
4 >>> ma_fonction(2)
5 x vaut 2 dans la fonction
6 >>> print(x)
7 Traceback (most recent call last):
8 File "<stdin>", line 1, in <module>
9 NameError: name 'x' is not defined

Lorsqu’une variable est déclarée dans le programme principal, elle est visible dans celui-ci ainsi que dans toutes les
fonctions. On a vu qu’on parlait de variable globale :
1 >>> def ma_fonction():
2 ... print(x)
3 ...
4 >>> x = 3
5 >>> ma_fonction()
6 3
7 >>> print(x)
8 3

Dans ce cas, la variable x est visible dans le module principal et dans toutes les fonctions du module. Toutefois,
Python ne permet pas la modification d’une variable globale dans une fonction :
1 >>> def ma_fonction():
2 ... x = x + 1
3 ...
4 >>> x = 1
5 >>> ma_fonction()
6 Traceback (most recent call last):
7 File "<stdin>", line 1, in <module>
8 File "<stdin>", line 2, in ma_fonction
9 UnboundLocalError: cannot access local variable 'x' where it is not associated with a value

132 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


13.4. Portée des listes Chapitre 13. Plus sur les fonctions

L’erreur renvoyée montre que Python pense que x est une variable locale qui n’a pas été encore assignée. Si on veut
vraiment modifier une variable globale dans une fonction, il faut utiliser le mot-clé global :
1 >>> def ma_fonction():
2 ... global x
3 ... x = x + 1
4 ...
5 >>> x = 1
6 >>> ma_fonction()
7 >>> x
8 2

Dans ce dernier cas, le mot-clé global a forcé la variable x à être globale plutôt que locale au sein de la fonction.

13.4 Portée des listes

Attention
Les exemples de cette partie représentent des absurdités en termes de programmation. Ils sont donnés à titre indicatif
pour comprendre ce qui se passe, mais il ne faut surtout pas s’en inspirer !

Soyez extrêmement attentifs avec les types modifiables (tels que les listes) car vous pouvez les changer au sein d’une
fonction :
1 >>> def ma_fonction():
2 ... liste1[1] = -127
3 ...
4 >>> liste1 = [1,2,3]
5 >>> ma_fonction()
6 >>> liste1
7 [1, -127, 3]

De même, si vous passez une liste en argument, elle est modifiable au sein de la fonction :
1 >>> def ma_fonction(liste_tmp):
2 ... liste_tmp[1] = -15
3 ...
4 >>> liste1 = [1,2,3]
5 >>> ma_fonction(liste1)
6 >>> liste1
7 [1, -15, 3]

Pour bien comprendre l’origine de ce comportement, utilisons à nouveau le site Python Tutor 3 . La figure 13.3 vous
montre le mécanisme à l’oeuvre lorsqu’on passe une liste à une fonction.
L’instruction pass dans la fonction est une instruction Python qui ne fait rien. Elle est là car une fonction ne peut
être vide et doit contenir au moins une instruction Python valide.
On voit très clairement que la variable liste1 passée en argument lors de l’appel de la fonction d’une part, et la
variable locale liste_tmp au sein de la fonction d’autre part, pointent vers le même objet dans la mémoire. Ainsi,
si on modifie liste_tmp, on modifie aussi liste1. C’est exactement le même mécanisme que pour la copie de listes
(cf. rubrique 11.4 Copie de listes du chapitre 12 Plus sur les listes).
Si vous voulez éviter les problèmes de modification malencontreuse d’une liste dans une fonction, utilisez des tuples (ils
ont présentés dans le chapitre 8 Dictionnaires et tuples), Python renverra une erreur car ces derniers sont non modifiables.
Une autre solution pour éviter la modification d’une liste, lorsqu’elle est passée comme argument à une fonction, est
de la passer explicitement (comme nous l’avons fait pour la copie de liste) afin qu’elle reste intacte dans le programme
principal :

3. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 133


Chapitre 13. Plus sur les fonctions 13.5. Règle LGI

Figure 13.3 – Passage d’une liste à une fonction.

1 >>> def ma_fonction(liste_tmp):


2 ... liste_tmp[1] = -15
3 ...
4 >>> liste1 = [1, 2, 3]
5 >>> ma_fonction(liste1[:])
6 >>> liste1
7 [1, 2, 3]
8 >>> ma_fonction(liste1(y))
9 >>> liste1
10 [1, 2, 3]

Dans ces deux derniers exemples, une copie de y est créée à la volée lorsqu’on appelle la fonction, ainsi la liste y du
module principal reste intacte.
D’autres suggestions sur l’envoi de liste dans une fonction vous sont données dans la rubrique Recommandations
ci-dessous.

13.5 Règle LGI


Lorsque Python rencontre une variable, il va traiter la résolution de son nom avec des priorités particulières. D’abord
il va regarder si la variable est locale, puis si elle n’existe pas localement, il vérifiera si elle est globale et enfin si elle
n’est pas globale, il testera si elle est interne (par exemple la fonction len() est considérée comme une fonction interne
à Python, elle existe à chaque fois que vous lancez Python). On appelle cela la règle LGI pour locale, globale, interne.
En voici un exemple :
1 >>> def ma_fonction():
2 ... x = 4
3 ... print(f"Dans la fonction x vaut {x}")
4 ...
5 >>> x = -15
6 >>> ma_fonction()
7 Dans la fonction x vaut 4
8 >>> print(f"Dans le module principal x vaut {x}")
9 Dans le module principal x vaut -15

Dans la fonction, x a pris la valeur qui lui était définie localement en priorité sur la valeur définie dans le module
principal.

Conseil
Même si Python accepte qu’une variable ait le même nom que ses propres fonctions ou variables internes, évitez

134 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


13.6. Recommandations Chapitre 13. Plus sur les fonctions

d’utiliser de tels noms, car ceci rendra votre code confus !

De manière générale, la règle LGI découle de la manière dont Python gère ce que l’on appelle « les espaces de noms ».
C’est cette gestion qui définit la portée (visibilité) de chaque variable. Nous en parlerons plus longuement dans le chapitre
24 Avoir plus la classe avec les objets (en ligne).

13.6 Recommandations
13.6.1 Évitez les variables globales
Dans ce chapitre nous avons joué avec les fonctions (et les listes) afin de vous montrer comment Python réagissait.
Toutefois, notez bien que l’utilisation de variables globales est à bannir définitivement de votre pratique de la
programmation.
Parfois on veut faire vite et on crée une variable globale visible partout dans le programme (donc dans toutes les
fonctions), car « Ça va plus vite, c’est plus simple ». C’est un très mauvais calcul, ne serait-ce que parce que vos fonctions
ne seront pas réutilisables dans un autre contexte si elles utilisent des variables globales ! Ensuite, arriverez-vous à vous
relire dans six mois ? Quelqu’un d’autre pourrait-il comprendre votre programme ? Il existe de nombreuses autres raisons 4
que nous ne développerons pas ici, mais libre à vous de consulter de la documentation externe.
Heureusement, Python est orienté objet et permet « d’encapsuler » des variables dans des objets et de s’affranchir
définitivement des variables globales (nous verrons cela dans le chapitre 23 Avoir la classe avec les objets). En attendant,
et si vous ne souhaitez pas aller plus loin sur les notions d’objet (on peut tout à fait « pythonner » sans cela), retenez la
chose suivante sur les fonctions et les variables globales :

Conseil
Plutôt que d’utiliser des variables globales, passez vos variables explicitement aux fonctions comme des argument(s).

13.6.2 Modification d’une liste dans une fonction


Concernant les fonctions qui modifient une liste, nous vous conseillons de l’indiquer clairement dans votre code. Pour
cela, faites en sorte que la fonction renvoie la liste modifiée et de récupérer cette liste renvoyée dans une variable portant
le même nom. Par exemple :
1 def ajoute_un(liste):
2 for indice in range(len(liste)):
3 liste[indice] += 1
4 return liste
5
6 # Programme principal.
7 liste_notes = [10, 8, 16, 7, 15]
8 liste_notes = ajoute_un(liste_notes)
9 print(liste_notes)

La ligne 8 indique que la liste liste_notes passée à la fonction est écrasée par la liste renvoyée par la fonction.
Le code suivant produirait la même sortie :
1 def ajoute_un(liste):
2 for indice in range(len(liste)):
3 liste[indice] += 1
4
5 # Programme principal.
6 liste_notes = [10, 8, 16, 7, 15]
7 ajoute_un(liste_notes)
8 print(liste_notes)

Cela reste toutefois moins intuitif, car il n’est pas évident de comprendre que la liste est modifiée dans la fonction
en lisant la ligne 7. Dans un tel cas, il serait essentiel d’indiquer dans la documentation de la fonction que la liste est
4. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 135


Chapitre 13. Plus sur les fonctions 13.7. Exercices

modifiée « sur place » (in place en anglais) dans la fonction. Vous verrez dans le chapitre 15 Création de modules
comment documenter vos fonctions.

Conseil
Pour les raisons évoquées ci-dessus, nous vous conseillons de privilégier la première version :
1 liste_notes = ajoute_un(liste_notes)

13.6.3 Conclusion
Vous connaissez maintenant les fonctions sous tous leurs angles. Comme indiqué en introduction du chapitre 10, elles
sont incontournables et tout programmeur se doit de les maîtriser. Voici les derniers conseils que nous pouvons vous
donner :
• Lorsque vous débutez un nouveau projet de programmation, posez-vous la question : « Comment pourrais-je
décomposer en blocs chaque tâche à effectuer, chaque bloc pouvant être une fonction ? ». Et n’oubliez pas que si
une fonction s’avère trop complexe, vous pouvez la décomposer en d’autres fonctions.
• Au risque de nous répéter, forcez-vous à utiliser des fonctions en permanence. Pratiquez, pratiquez… et pratiquez
encore !

13.7 Exercices

Conseil
Pour le second exercice, créez un script puis exécutez-le dans un shell.

13.7.1 Prédire la sortie


Prédisez le comportement des codes suivants, sans les recopier dans un script ni dans l’interpréteur Python :

[Link] Code 1

1 def hello(prenom):
2 print(f"Bonjour {prenom}")
3
4
5 # Programme principal.
6 hello("Patrick")
7 print(x)

[Link] Code 2

1 def hello(prenom):
2 print(f"Bonjour {prenom}")
3
4
5 # Programme principal.
6 x = 10
7 hello("Patrick")
8 print(x)

136 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


13.7. Exercices Chapitre 13. Plus sur les fonctions

[Link] Code 3

1 def hello(prenom):
2 print(f"Bonjour {prenom}")
3 print(x)
4
5
6 # Programme principal.
7 x = 10
8 hello("Patrick")
9 print(x)

[Link] Code 4

1 def hello(prenom):
2 x = 42
3 print(f"Bonjour {prenom}")
4 print(x)
5
6
7 # Programme principal.
8 x = 10
9 hello("Patrick")
10 print(x)

13.7.2 Passage de liste à une fonction


Créez une fonction ajoute_nb_alea() qui prend en argument une liste et qui ajoute un nombre entier aléatoire
entre -10 et 10 (inclus) à chaque élément. La fonction affichera à l’écran cette nouvelle liste modifiée.
Dans le programme principal, effectuez les actions suivantes :
1. Créez une variable ma_liste = [7, 3, 8, 4, 5, 1, 9, 10, 2, 6].
2. Affichez ma_liste à l’écran.
3. Appelez la fonction ajoute_nb_alea() en lui passant ma_liste en argument.
4. Affichez à nouveau ma_liste à l’écran.
Comment expliquez-vous le résultat obtenu ?

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 137


CHAPITRE 14

Conteneurs

Dans ce chapitre, nous allons aborder la notion de conteneur, revenir sur certaines propriétés avancées des dictionnaires
et tuples, et enfin aborder les types set et frozenset. Pour les débutants, ce chapitre aborde des notions relativement
avancées. Avant de vous lancer, nous vous conseillons vivement de bien maitriser les chapitres 4 Listes et 12 Plus sur les
listes, ainsi que le chapitre 8 Dictionnaires et tuples, d’avoir effectué un maximum d’exercices, et de vous sentir à l’aise
avec toutes les notions abordées jusque là.

14.1 Généralités
14.1.1 Définition et propriétés

Définition
Un conteneur (container en anglais) est un nom générique pour définir un objet Python qui contient une collection
d’autres objets.

Les conteneurs que nous connaissons depuis le début de ce cours sont les listes, les chaînes de caractères, les diction-
naires et les tuples. Même si on ne l’a pas vu explicitement, les objets de type range sont également des conteneurs.
Dans la suite de cette rubrique, nous allons examiner les différentes propriétés des conteneurs. À la fin de ce chapitre,
nous ferons un tableau récapitulatif de ces propriétés.
Examinons d’abord les propriétés qui caractérisent tous les types de conteneur.
• Capacité à supporter le test d’appartenance. Souvenez-vous, il permet de vérifier si un élément était présent dans
une liste. Cela fonctionne donc aussi sur les chaînes de caractères ou tout autre conteneur :
1 >>> liste1 = [4, 5, 6]
2 >>> 4 in liste1
3 True
4 >>> "to" in "toto"
5 True

• Capacité à supporter la fonction len() renvoyant la longueur du conteneur.


Voici d’autres propriétés générales que nous avons déjà croisées. Un conteneur peut être :
• Ordonné (ordered en anglais) : il y a un ordre précis des éléments ; cet ordre correspond à celui utilisé lors de la
création ou de la modification du conteneur (si cela est permis) ; ce même ordre est utilisé lorsqu’on itère dessus.

138
14.1. Généralités Chapitre 14. Conteneurs

• Indexable (subscriptable en anglais) : on peut retrouver un élément par son indice (c’est-à-dire sa position dans le
conteneur) ou plusieurs éléments avec une tranche ; en général, tout conteneur indexable est ordonné.
• Itérable (iterable en anglais) : on peut faire une boucle dessus.
Certains conteneurs sont appelés objets séquentiels ou séquence.

Définition
Un objet séquentiel ou séquence est un conteneur itérable, ordonné et indexable. Les objets séquentiels sont les
listes, les chaînes de caractères, les objets de type range, ainsi que les tuples.

Une autre propriété importante que l’on a déjà croisée, et qui nous servira dans ce chapitre, concerne la possibilité
ou non de modifier un objet.
• Un objet est dit non modifiable lorsqu’on ne peut pas le modifier, ou lorsqu’on ne peut pas en modifier un de
ses éléments si c’est un conteneur. On parle aussi d’objet immuable 1 (immutable object en anglais). Cela signifie
qu’une fois créé, Python ne permet plus de le modifier par la suite.
Qu’en est-il des objets que nous connaissons ? Les listes sont modifiables, on peut modifier un ou plusieurs de ses
éléments et ajouter ou retirer un élément. Les dictionnaires sont modifiables : pour une clé donnée, on peut changer la
valeur correspondante et ajouter ou retirer un couple clé/valeur. Tous les autres types que nous avons vus précédemment
sont quant à eux non modifiables : les chaînes de caractères ou strings, les tuples, les objets de type range, mais également
des objets qui ne sont pas des conteneurs comme les entiers, les floats et les booléens.
On comprend bien l’immutabilité des strings comme vu au chapitre 11 Plus sur les chaînes de caractères, mais c’est
moins évident pour les entiers, floats ou booléens. Nous allons démontrer cela, mais avant nous avons besoin de définir
la notion d’identifiant d’un objet.

Définition
L’identifiant d’un objet est un nombre entier qui est garanti constant pendant toute la durée de vie de l’objet. Cet
identifiant est en général unique pour chaque objet. Toutefois, pour des raisons d’optimisation, Python crée parfois le
même identifiant pour deux objets non modifiables différents qui ont la même valeur. L’identifiant peut être assimilé
à l’adresse mémoire de l’objet qui, elle aussi, est unique. En Python, on utilise la fonction interne id() qui prend en
argument un objet et renvoie son identifiant.

Maintenant que l’identifiant est défini, regardons l’exemple suivant qui montre l’immutabilité des entiers :
1 >>> var = 4
2 >>> id(var)
3 140318876873440
4 >>> var = 5
5 >>> id(var)
6 140318876873472

Ligne 1 on définit l’entier var puis on regarde son identifiant.


Ligne 4, on pourrait penser que l’on modifie var. Toutefois, on voit que son identifiant ligne 6 est différent de la
ligne 3. En fait, l’affectation ligne 4 var = 5 écrase l’ancienne variable var et en crée une nouvelle, ce n’est pas la valeur
de var qui a été changée puisque l’identifiant n’est plus le même. Le même raisonnement peut être tenu pour les autres
types numériques comme les floats et booléens.
Si on regarde maintenant ce qu’il se passe pour une liste :
1 >>> liste1 = [1, 2, 3]
2 >>> id(liste1)
3 140318850324832
4 >>> liste1[1] = -15
5 >>> id(liste1)
6 140318850324832
7 >>> [Link](5)
8 >>> id(liste1)
9 140318850324832

1. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 139


Chapitre 14. Conteneurs 14.1. Généralités

La liste liste1 a été modifiée ligne 4 (changement de l’élément d’indice 1) et ligne 7 (ajout d’un élément). Pour
autant, l’identifiant de cette liste est resté identique tout du long. Ceci démontre la mutabilité des listes : quelle que soit
la manière dont on modifie une liste, celle-ci garde le même identifiant.
• Une dernière propriété importante est la capacité d’un conteneur (ou tout autre objet Python) à être hachable.

Définition
Un objet Python est dit hachable (hashable en anglais) s’il est possible de calculer une valeur de hachage sur celui-ci
avec la fonction interne hash(). En programmation, la valeur de hachage peut être vue comme une empreinte numérique
de l’objet. Elle est obtenue en passant l’objet dans une fonction de hachage et dépend du contenu de l’objet. En Python,
cette empreinte est, comme dans la plupart des langages de programmation, un entier. Au sein d’une même session
Python, deux objets hachables qui ont un contenu identique auront strictement la même valeur de hachage.

Attention
La valeur de hachage d’un objet renvoyée par la fonction hash() n’a pas le même sens que son identifiant renvoyé
par la fonction id(). La valeur de hachage est obtenue en « moulinant » le contenu de l’objet dans une fonction de
hachage. L’identifiant est quant à lui attribué par Python à la création de l’objet. Il est constant tout le long de la durée
de vie de l’objet, un peu comme une carte d’identité. Tout objet a un identifiant, mais il doit être hachable pour avoir
une valeur de hachage.

Pour aller plus loin


Pour aller plus loin, vous pouvez consulter la page Wikipedia sur les fonctions de hachage 2 .

Pourquoi évoquer cette propriété de hachabilité ? D’abord, parce qu’elle est étroitement liée à l’immutabilité. En effet,
un objet non modifiable est la plupart du temps hachable. Cela permet de l’identifier en fonction de son contenu. Par
ailleurs, l’hachabilité est une implémentation qui permet un accès rapide aux éléments des conteneurs de type dictionnaire
ou set (cf. rubriques suivantes).
Les objets hachables sont les chaînes de caractères, les entiers, les floats, les booléens, les objets de type range, les
tuples (sous certaines conditions) et les frozensets ; par contre, les listes, les sets et les dictionnaires sont non hachables.
Les sets et frozensets seront vus plus bas dans ce chapitre.
Voici un exemple :
1 >>> hash("Plouf")
2 5085648805260210718
3 >>> hash(5)
4 5
5 >>> hash(3.14)
6 322818021289917443
7 >>> hash([1, 2, 3])
8 Traceback (most recent call last):
9 File "<stdin>", line 1, in <module>
10 TypeError: unhashable type: 'list'

Les valeurs de hachage renvoyées par la fonction hash() de Python sont systématiquement des entiers. Par contre,
Python renvoie une erreur pour une liste, car elle est non hachable.

14.1.2 Conteneurs de type range


Revenons rapidement sur les objets de type range. Jusqu’à maintenant, on s’en est servi pour faire des boucles ou
générer des listes de nombres. Toutefois, on a vu ci-dessus qu’ils étaient aussi des conteneurs. Ils sont ordonnés, indexables,
itérables, hachables et non modifiables :

2. [Link]

140 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


14.2. Plus sur les dictionnaires Chapitre 14. Conteneurs

1 >>> range1 = range(3)


2 >>> range1[0]
3 0
4 >>> range1[0:1]
5 range(0, 1)
6 >>> for element in range1:
7 ... print(element)
8 ...
9 0
10 1
11 2
12 >>> range1[2] = 10
13 Traceback (most recent call last):
14 File "<stdin>", line 1, in <module>
15 TypeError: 'range' object does not support item assignment
16 >>> hash(range1)
17 5050907061201647097

La tentative de modification d’un élément ligne 12 conduit à la même erreur que lorsqu’on essaie de modifier un
caractère d’une chaîne de caractères. Comme pour la plupart des objets Python non modifiables, les objets de type range
sont hachables.

14.2 Plus sur les dictionnaires


Nous revenons sur les dictionnaires qui, on l’a vu, sont des conteneurs de correspondance où chaque valeur est associée
à une clé plutôt qu’un indice. Nous allons voir certaines propriétés avancées des dictionnaires, notamment comment trier
par clé ou par valeur.

14.2.1 Objets utilisables comme clé


Toutes les clés de dictionnaire vues dans le chapitre 8 Dictionnaires et tuples et utilisées jusqu’à présent étaient des
chaînes de caractères. Toutefois, on peut utiliser d’autres types d’objets comme des entiers, des floats, voire des tuples,
cela peut s’avérer parfois très utile. Une règle est toutefois requise : les objets utilisés comme clé doivent être hachables
(voir la rubrique précédente pour la définition).
Pourquoi les clés doivent être des objets hachables ? C’est la raison d’être des dictionnaires qui d’ailleurs sont aussi
appelés table de hachage 3 dans d’autres langages, comme Perl. Convertir chaque clé en sa valeur de hachage permet
un accès très rapide à chacun des éléments du dictionnaire, ainsi que des comparaisons de clés entre dictionnaires
extrêmement efficaces. Même si on a vu que deux objets pouvaient avoir la même valeur de hachage, par exemple a = 5
et b = 5, on ne peut mettre qu’une seule fois la clé 5. Ceci assure que deux clés d’un même dictionnaire ont forcément
une valeur de hachage différente.
Pouvoir utiliser autre chose qu’une chaîne de caractères comme clé peut se révéler très pratique. Par exemple, pour
une protéine ou un peptide, on pourrait concevoir d’utiliser comme clé le numéro de résidu, et comme valeur le nom de
résidu. Imaginons par ailleurs que nous commencions à compter le premier acide aminé à 3 (souvent les fichiers PDB ne
commence pas à 1 pour le premier acide aminé). Par exemple :
1 >>> sequence = {3: 'S', 4: 'E', 5: 'Q', 6: 'P', 7: 'E', 8: 'P', 9: 'T'}
2 >>> sequence[5]
3 'Q'
4 >>> sequence[9]
5 'T'
6 >>> for num, res in [Link]():
7 ... print(num, res)
8 ...
9 3 S
10 4 E
11 5 Q
12 6 P
13 7 E
14 8 P
15 9 T

3. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 141


Chapitre 14. Conteneurs 14.2. Plus sur les dictionnaires

Vous voyez l’énorme avantage, d’utiliser comme clé le numéro de résidu. Avec une liste ou une chaîne de caractère,
l’indiçage commence à zéro. Ainsi, il faudrait utiliser les indices 2 et 6 pour retrouver respectivement les acides aminés 5
et 9 :
1 >>> sequence = ['S', 'E', 'Q', 'P', 'E', 'P', 'T']
2 >>> sequence[2]
3 'Q'
4 >>> sequence[6]
5 'T'

14.2.2 Destruction d’une paire clé/valeur


Comme pour tous les objets Python, l’instruction del permet de détruire un couple clé/valeur :
1 >>> dico = {'nom': 'girafe', 'taille': 5.0, 'poids': 1100}
2 >>> del dico["nom"]
3 >>> dico
4 {'taille': 5.0, 'poids': 1100}

Pour les listes, on utilise l’indice entre crochet pour détruire l’élément, par exemple del liste[2]. Ici, on utilise la
clé.

14.2.3 Tri par clés


On peut utiliser la fonction sorted() vue précédemment avec les listes pour trier un dictionnaire par ses clés :
1 >>> ani2 = {'nom': 'singe', 'taille': 1.75, 'poids': 70}
2 >>> sorted(ani2)
3 ['nom', 'poids', 'taille']

Les clés sont triées ici par ordre alphabétique.

14.2.4 Tri par valeurs


Pour trier un dictionnaire par ses valeurs, il faut utiliser la fonction sorted() avec l’argument key :
1 >>> dico = {"a": 15, "b": 5, "c":20}
2 >>> sorted(dico, key=[Link])
3 ['b', 'a', 'c']

L’argument key=[Link] indique explicitement qu’il faut réaliser le tri par les valeurs du dictionnaire. On retrouve
la méthode .get() vue au chapitre 8 Dictionnaires et tuples, mais sans les parenthèses : key=[Link], mais pas key
=[Link](). Une fonction ou méthode passée en argument sans les parenthèses est appelée callback, nous reverrons
cela en détail dans le chapitre 25 Fenêtres graphiques et Tkinter (en ligne).
Attention, ce sont les clés du dictionnaire qui sont renvoyées, pas les valeurs. Ces clés sont cependant renvoyées dans
un ordre qui permet d’obtenir les clés triées par ordre croissant :
1 >>> dico = {"a": 15, "b": 5, "c":20}
2 >>> for key in sorted(dico, key=[Link]):
3 ... print(key, dico[key])
4 ...
5 b 5
6 a 15
7 c 20

Enfin, l’argument reverse=True fonctionne également :


1 >>> dico = {"a": 15, "b": 5, "c":20}
2 >>> sorted(dico, key=[Link], reverse=True)
3 ['c', 'a', 'b']

Remarque
Lorsqu’on trie un dictionnaire par ses valeurs, il faut être sûr que cela soit possible. Ce n’est pas le cas lorsqu’on a un
mélange de valeurs numériques et chaînes de caractères :

142 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


14.2. Plus sur les dictionnaires Chapitre 14. Conteneurs

1 >>> ani2 = {'nom': 'singe', 'poids': 70, 'taille': 1.75}


2 >>> sorted(ani2, key=[Link])
3 Traceback (most recent call last):
4 File "<stdin>", line 1, in <module>
5 TypeError: '<' not supported between instances of 'int' and 'str'

On obtient ici une erreur, car Python ne sait pas comparer une chaîne de caractères (singe) avec des valeurs
numériques (70 et 1.75).

14.2.5 Clé associée au minimum ou au maximum des valeurs


Les fonctions min() et max(), que vous avez déjà manipulées dans les chapitres précédents, acceptent également
l’argument key=. On peut ainsi obtenir la clé associée au minimum ou au maximum des valeurs d’un dictionnaire :
1 >>> dico = {"a": 15, "b": 5, "c":20}
2 >>> max(dico, key=[Link])
3 'c'
4 >>> min(dico, key=[Link])
5 'b'

14.2.6 Fonction dict()


La fonction dict() va convertir l’argument qui lui est passé en dictionnaire. Il s’agit donc d’une fonction de casting,
comme int(), str(), etc. Toutefois, l’argument qui lui est passé doit avoir une forme particulière : un objet séquentiel
contenant d’autres objets séquentiels de deux éléments. Par exemple, une liste de listes de deux éléments :
1 >>> liste_animaux = [["girafe", 2], ["singe", 3]]
2 >>> dict(liste_animaux)
3 {'girafe': 2, 'singe': 3}

Ou un tuple de tuples de deux éléments, ou encore une combinaison liste et tuple :


1 >>> tuple_animaux = (("girafe", 2), ("singe", 3))
2 >>> dict(tuple_animaux)
3 {'girafe': 2, 'singe': 3}
4 >>>
5 >>> dict([("girafe", 2), ("singe", 3)])
6 {'girafe': 2, 'singe': 3}

Si un des sous-éléments a plus de deux éléments (ou moins), Python renvoie une erreur :
1 >>> dict([("girafe", 2), ("singe", 3, 4)])
2 Traceback (most recent call last):
3 File "<stdin>", line 1, in <module>
4 ValueError: dictionary update sequence element #1 has length 3; 2 is required

Attention
Une manière intuitive utilise simplement des arguments par mot-clés, qui deviendront des clés sous forme de chaîne
de caractères :
1 >>> dict(un=1, deux=2, trois=3)
2 {'un': 1, 'deux': 2, 'trois': 3}

Nous vous déconseillons toutefois cette manière de faire, car on ne peut pas mettre d’arguments par mot-clé variables,
on doit les écrire explicitement.

Une dernière manière puissante pour générer des dictionnaires combine les fonctions dict() et zip(). On se souvient
que la fonction zip() peut générer une liste de tuples :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 143


Chapitre 14. Conteneurs 14.3. Plus sur les tuples

1 >>> animaux = ["poulain", "renard", "python"]


2 >>> couleurs = ["alezan", "roux", "vert"]
3 >>> list(zip(animaux, couleurs))
4 [('poulain', 'alezan'), ('renard', 'roux'), ('python', 'vert')]

Si on utilise l’objet zip avec la fonction dict(), on obtient un dictionnaire.


1 >>> dict(zip(animaux, couleurs))
2 {'poulain': 'alezan', 'renard': 'roux', 'python': 'vert'}

Attention à ne passer que deux listes à la fonction zip(), sinon Python renvoie une erreur :
1 >>> dict(zip([1, 2, 3], animaux, couleurs))
2 Traceback (most recent call last):
3 File "<stdin>", line 1, in <module>
4 ValueError: dictionary update sequence element #0 has length 3; 2 is required

14.3 Plus sur les tuples


Nous revenons sur les tuples, que nous avons défini dans le chapitre 8 Dictionnaires et tuples et que nous avons
croisé à de nombreuses reprises, notamment avec les fonctions. Les tuples sont des objets séquentiels correspondant aux
listes, donc ils sont itérables, ordonnés et indexables, mais ils sont toutefois non modifiables. On verra plus bas qu’ils
sont hachables sous certaines conditions. L’intérêt des tuples par rapport aux listes réside dans leur immutabilité. Cela
accélère considérablement la manière dont Python accède à chaque élément et ils prennent moins de place en mémoire.
Par ailleurs, on ne risque pas de modifier un de ses éléments par mégarde.

14.3.1 Immutabilité
Nous avions vu que les tuples étaient immuables :
1 >>> tuple1 = (1, 2, 3)
2 >>> tuple1[2] = 15
3 Traceback (most recent call last):
4 File "<stdin>", line 1, in <module>
5 TypeError: 'tuple' object does not support item assignment

Ce message est similaire à celui que nous avions rencontré quand on essayait de modifier une chaîne de caractères
(voir chapitre 11 Plus sur les chaînes de caractères). De manière générale, Python renverra un message TypeError: '
[...]' does not support item assignment lorsqu’on essaie de modifier un élément d’un objet non modifiable. Si
vous voulez ajouter un élément (ou le modifier), vous devez créer un nouveau tuple :
1 >>> tuple1 = (1, 2, 3)
2 >>> tuple1
3 (1, 2, 3)
4 >>> id(tuple1)
5 139971081704464
6 >>> tuple1 = tuple1 + (2,)
7 >>> tuple1
8 (1, 2, 3, 2)
9 >>> id(tuple1)
10 139971081700368

La fonction id() montre que le tuple créé ligne 6 est bien différent de celui créé ligne 4, bien qu’ils aient le même
nom. Comme on a vu plus haut, ceci est dû à l’opérateur d’affectation utilisé ligne 6 (tuple1 = tuple1 + (2,)) qui
crée un nouvel objet distinct de celui de la ligne 1. Cet exemple montre que les tuples sont peu adaptés lorsqu’on a besoin
d’ajouter, retirer, modifier des éléments. La création d’un nouveau tuple à chaque étape s’avère lourde et il n’y a aucune
méthode pour faire cela, puisque les tuples sont non modifiables.

Conseil
Pour ce genre de tâche, les listes sont clairement mieux adaptées que les tuples.

144 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


14.3. Plus sur les tuples Chapitre 14. Conteneurs

14.3.2 Affectation multiple et fonctions


Dans le chapitre 8 Dictionnaires et tuples, nous avons abordé l’affectation multiple. Pour rappel, elle permet d’effectuer
sur une même ligne plusieurs affectations en même temps, par exemple : x, y, z = 1, 2, 3. On a vu qu’il était possible
de le faire également avec les listes : [x, y, z] = [1, 2, 3]. Toutefois, cette syntaxe étant alourdie par la présence
des crochets, on préfèrera toujours la première syntaxe avec les tuples sans parenthèses.
Concernant les fonctions, nous avions croisé l’importance de l’affectation multiple dans le chapitre 10 lorsqu’une
fonction renvoyait plusieurs valeurs :
1 >>> def ma_fonction():
2 ... return 3, 14
3 ...
4 >>> x, y = ma_fonction()
5 >>> print(x, y)
6 3 14

La syntaxe x, y = ma_fonction() permet de récupérer les deux valeurs renvoyées par la fonction et de les affecter
à la volée dans deux variables différentes. Cela évite l’opération laborieuse de récupérer d’abord le tuple, puis de créer les
variables en utilisant l’indiçage :
1 >>> resultat = ma_fonction()
2 >>> resultat
3 (3, 14)
4 >>> x = resultat[0]
5 >>> y = resultat[1]
6 >>> print(x, y)
7 3 14

Conseil
Lorsqu’une fonction renvoie plusieurs valeurs sous forme de tuple, privilégiez toujours la forme x, y = ma_fonction
().

14.3.3 Affectation multiple et nom de variable _


Quand une fonction renvoie plusieurs valeurs, mais que l’on ne souhaite pas les utiliser toutes dans la suite du code,
on peut utiliser le nom de variable _ (caractère underscore) pour indiquer que certaines valeurs ne nous intéressent pas :
1 >>> def ma_fonction():
2 ... return 1, 2, 3, 4
3 ...
4 >>> x, _, y, _ = ma_fonction()
5 >>> x
6 1
7 >>> y
8 3

Cela envoie le message à la personne qui lit le code « je ne m’intéresse pas aux valeurs récupérées dans les variables
_ ». Notez que l’on peut utiliser une ou plusieurs variables underscore(s). Dans l’exemple ci-dessus, la 2e et la 4e variable
renvoyées par la fonction seront ignorées dans la suite du code. Cela présente le mérite d’éviter de polluer l’attention de
la personne qui lit le code.

Remarque
Dans l’interpréteur interactif, la variable _ a une signification différente. Elle prend automatiquement la dernière valeur
affichée :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 145


Chapitre 14. Conteneurs 14.3. Plus sur les tuples

1 >>> 3
2 3
3 >>> _
4 3
5 >>> "mésange"
6 'mésange'
7 >>> _
8 'mésange'

Attention, ceci n’est vrai que dans l’interpréteur !

Remarque
Le caractère underscore (_) est couramment utilisé dans les noms de variable pour séparer les mots et être explicite,
par exemple seq_ADN ou liste_listes_residus. On verra dans le chapitre 16 Bonnes pratiques en programmation
Python que ce style de nommage est appelé snake_case. Toutefois, il faut éviter d’utiliser les underscores en début et/ou
en fin de nom de variable (leading et trailing underscores en anglais), par exemple : _var, var_, __var, __var__. On
verra au chapitre 23 Avoir la classe avec les objets que ces underscores ont aussi une signification particulière.

14.3.4 Tuples contenant des listes


On a vu que les tuples étaient non modifiables. Que se passe-t-il alors si on crée un tuple contenant des objets
modifiables comme des listes ? Examinons le code suivant :
1 >>> liste1 = [1, 2, 3]
2 >>> tuple1 = (liste1, "Plouf")
3 >>> tuple1
4 ([1, 2, 3], 'Plouf')
5 >>> liste1[0] = -15
6 >>> tuple1[0].append(-632)
7 >>> tuple1
8 ([-15, 2, 3, -632], 'Plouf')

Si on modifie un élément de la liste liste1 (ligne 5) ou bien qu’on ajoute un élément à tuple1[0] (ligne 6), Python
s’exécute et ne renvoie pas de message d’erreur. Or nous avions dit qu’un tuple était non modifiable… Comment cela
est-il possible ? Commençons d’abord par regarder comment les objets sont agencés avec Python Tutor.

Figure 14.1 – Tuple contenant une liste.

La liste liste1 pointe vers le même objet que l’élément du tuple d’indice 0. Comme pour la copie de liste (par
exemple liste_b = liste_a), ceci est attendu car, par défaut, Python crée une copie par référence (voir le chapitre
12 Plus sur les listes). Ainsi, qu’on raisonne en tant que premier élément du tuple ou bien en tant que liste liste1, on
pointe vers la même liste. Or, rappelez-vous, nous avons expliqué au début de ce chapitre que lorsqu’on modifiait un

146 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


14.3. Plus sur les tuples Chapitre 14. Conteneurs

élément d’une liste, celle-ci gardait le même identifiant. C’est toujours le cas ici, même si celle-ci se trouve dans un tuple.
Regardons cela :
1 >>> liste1 = [1, 2, 3]
2 >>> tuple1 = (liste1, "Plouf")
3 >>> tuple1
4 ([1, 2, 3], 'Plouf')
5 >>> id(liste1)
6 139971081980816
7 >>> id(tuple1[0])
8 139971081980816

Nous confirmons ici le schéma de Python Tutor, c’est bien la même liste que l’on considère liste1 ou tuple1[0]
puisqu’on a le même identifiant. Maintenant, on modifie cette liste via la variable liste1 ou tuple1[0] :
1 >>> liste1[2] = -15
2 >>> tuple1[0].append(-632)
3 >>> tuple1
4 ([1, 2, -15, -632], 'Plouf')
5 >>> id(liste1)
6 139971081980816
7 >>> id(tuple1[0])
8 139971081980816

Malgré la modification de cette liste, l’identifiant n’a toujours pas changé puisque la fonction id() nous renvoie la
même valeur depuis le début. Même si la liste a été modifiée « de l’intérieur », Python considère que c’est toujours la
même liste, puisqu’elle n’a pas changé d’identifiant. Si au contraire on essaie de remplacer cette sous-liste par autre
chose, Python renvoie une erreur :
1 >>> tuple1[0] = "Plif"
2 Traceback (most recent call last):
3 File "<stdin>", line 1, in <module>
4 TypeError: 'tuple' object does not support item assignment

Cette erreur s’explique par le fait que le nouvel objet "Plif" n’a pas le même identifiant que la sous-liste initiale. En
fait, l’immutabilité selon Python signifie qu’un objet créé doit toujours garder le même identifiant. Cela est valable pour
tout objet non modifiable, comme un élément d’un tuple, un caractère dans une chaîne de caractères, etc.

Conseil
Cette digression avait pour objectif de vous faire comprendre ce qu’il se passe lorsqu’on met une liste dans un tuple.
Toutefois, pouvoir modifier une liste en tant qu’élément d’un tuple va à l’encontre de l’intérêt d’un objet non modifiable.
Dans la mesure du possible, nous vous déconseillons de créer des listes dans des tuples afin d’éviter les déconvenues.

14.3.5 Fonction tuple()


Nous avions vu également la fonction tuple(), qui permet de convertir un objet séquentiel en tuple (opération de
casting). Cela est possible seulement si l’objet passé en argument est itérable :
1 >>> tuple([1, 3])
2 (1, 3)
3 >>> tuple("a")
4 ('a',)
5 >>> tuple(2)
6 Traceback (most recent call last):
7 File "<stdin>", line 1, in <module>
8 TypeError: 'int' object is not iterable
9 >>> tuple(True)
10 Traceback (most recent call last):
11 File "<stdin>", line 1, in <module>
12 TypeError: 'bool' object is not iterable

Bien sûr, un entier ou un booléen ne sont pas itérables.

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 147


Chapitre 14. Conteneurs 14.4. Sets et frozensets

14.3.6 Hachabilité des tuples


Les tuples sont hachables s’ils ne contiennent que des éléments hachables. Si un tuple contient un ou plusieurs objet(s)
non hachable(s), comme une liste, il devient non hachable :
1 >>> tuple1 = tuple(range(10))
2 >>> tuple1
3 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
4 >>> hash(tuple1)
5 -4181190870548101704
6 >>> tuple2 = ("Plouf", 2, (1, 3))
7 >>> tuple2
8 ('Plouf', 2, (1, 3))
9 >>> hash(tuple2)
10 286288423668065022
11 >>> tuple3 = (1, (3, 4), "Plaf", [3, 4, 5])
12 >>> tuple3
13 (1, (3, 4), 'Plaf', [3, 4, 5])
14 >>> hash(tuple3)
15 Traceback (most recent call last):
16 File "<stdin>", line 1, in <module>
17 TypeError: unhashable type: 'list'

Les tuples tuple1 et tuple2 sont hachables car ils ne contiennent que des éléments hachables. Par contre, tuple3
ne l’est pas, car un de ses éléments est une liste.

Conseil
Mettre une ou plusieurs liste(s) dans un tuple le rend non hachable. Ceci le rend inutilisable comme clé de dictionnaire
ou, on le verra ci-après, comme élément d’un set ou d’un frozenset. Donc, à nouveau, ne mettez pas de listes dans vos
tuples !

14.4 Sets et frozensets


14.4.1 Définition et propriétés
Les objets de type set représentent un autre type de conteneur qui peut se révéler très pratique. Ils ont la particularité
d’être modifiables, non hachables, non ordonnés, non indexables et de ne contenir qu’une seule copie maximum de chaque
élément. Pour créer un nouveau set on peut utiliser les accolades :
1 >>> set1 = {4, 5, 5, 12}
2 >>> set1
3 {12, 4, 5}
4 >>> type(set1)
5 <class 'set'>

Remarquez que la répétition du chiffre 5 dans la définition du set ligne 1 produit finalement un seul chiffre 5, car
chaque élément ne peut être présent qu’une seule fois. Comme pour les dictionnaires (jusqu’à la version 3.6), les sets
sont non ordonnés. La manière dont Python les affiche n’a pas de sens en tant que tel et peut être différente de celle
utilisée lors de leur création.
Les sets ne peuvent contenir que des objets hachables. On a déjà eu le cas avec les clés de dictionnaire. Ceci optimise
l’accès à chaque élément du set. Pour rappel, les objets hachables que nous connaissons sont les chaînes de caractères,
les tuples, les entiers, les floats, les booléens et les frozensets (voir plus bas). Les objets non hachables que l’on connait
sont les listes, les sets et les dictionnaires. Si on essaie tout de même de mettre une liste dans un set, Python renvoie
une erreur :
1 >>> set1 = {3, 4, "Plouf", (1, 3)}
2 >>> set1
3 {(1, 3), 3, 4, 'Plouf'}
4 >>> set2 = {3.14, [1, 2]}
5 Traceback (most recent call last):
6 File "<stdin>", line 1, in <module>
7 TypeError: unhashable type: 'list'

148 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


14.4. Sets et frozensets Chapitre 14. Conteneurs

À quoi différencie-t-on un set d’un dictionnaire alors que les deux utilisent des accolades ? Le set sera défini seulement
par des valeurs {valeur_1, valeur_2, ...} alors que le dictionnaire aura toujours des couples clé/valeur {clé_1:
valeur_1, clé_2: valeur_2, ...}.
La fonction interne à Python set() convertit un objet itérable passé en argument en un nouveau set (opération de
casting) :
1 >>> set([1, 2, 4, 1])
2 {1, 2, 4}
3 >>> set((2, 2, 2, 1))
4 {1, 2}
5 >>> set(range(5))
6 {0, 1, 2, 3, 4}
7 >>> set({"clé_1": 1, "clé_2": 2})
8 {'clé_1', 'clé_2'}
9 >>> set(["ti", "to", "to"])
10 {'ti', 'to'}
11 >>> set("Maître Corbeau et Maître Renard")
12 {'e', 'd', 'M', 'r', 'n', 't', 'a', 'C', 'î', ' ', 'o', 'u', 'R', 'b'}

Nous avons dit plus haut que les sets ne sont ni ordonnés ni indexables, il est donc impossible de récupérer un élément
par sa position. Il est également impossible de modifier un de ses éléments par l’indexation.
1 >>> set1 = set([1, 2, 4, 1])
2 >>> set1[1]
3 Traceback (most recent call last):
4 File "<stdin>", line 1, in <module>
5 TypeError: 'set' object is not subscriptable
6 >>> set1[1] = 5
7 Traceback (most recent call last):
8 File "<stdin>", line 1, in <module>
9 TypeError: 'set' object does not support item assignment

Par contre, les sets sont itérables :


1 >>> for element in set1:
2 ... print(element)
3 ...
4 1
5 2
6 4

Les sets ne peuvent être modifiés que par des méthodes spécifiques :
1 >>> set1 = set(range(5))
2 >>> set1
3 {0, 1, 2, 3, 4}
4 >>> [Link](4)
5 >>> set1
6 {0, 1, 2, 3, 4}
7 >>> [Link](472)
8 >>> set1
9 {0, 1, 2, 3, 4, 472}
10 >>> [Link](0)
11 >>> set1
12 {1, 2, 3, 4, 472}

La méthode .add() ajoute au set l’élément passé en argument. Toutefois, si l’élément est déjà présent dans le set,
il n’est pas ajouté puisqu’on a au plus une copie de chaque élément. La méthode .discard() retire du set l’élément
passé en argument. Si l’élément n’est pas présent dans le set, il ne se passe rien, le set reste intact. Comme les sets ne
sont pas ordonnés ni indexables, il n’y a pas de méthode pour insérer un élément à une position précise, contrairement
aux listes. Dernier point sur ces méthodes, elles modifient le set sur place (in place, en anglais) et ne renvoient rien, à
l’instar des méthodes des listes (.append(), .remove(), etc.).
Enfin, les sets ne supportent pas les opérateurs + et *.

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 149


Chapitre 14. Conteneurs 14.4. Sets et frozensets

14.4.2 Utilité

Les conteneurs de type set sont très utiles pour rechercher les éléments uniques d’une suite d’éléments. Cela revient
à éliminer tous les doublons. Par exemple :

1 >>> import random


2 >>> liste1 = [[Link](0, 9) for i in range(10)]
3 >>> liste1
4 [7, 9, 6, 6, 7, 3, 8, 5, 6, 7]
5 >>> set(liste1)
6 {3, 5, 6, 7, 8, 9}

On peut bien sûr transformer dans l’autre sens un set en liste. Cela permet par exemple d’éliminer les doublons de la
liste initiale, tout en récupérant une liste à la fin :

1 >>> list(set([7, 9, 6, 6, 7, 3, 8, 5, 6, 7]))


2 [3, 5, 6, 7, 8, 9]

On peut faire des choses très puissantes. Par exemple, un compteur de lettres en combinaison avec une liste de
compréhension, le tout en une ligne !

1 >>> seq = "atctcgatcgatcgcgctagctagctcgccatacgtacgactacgt"


2 >>> set(seq)
3 {'c', 'g', 't', 'a'}
4 >>> [(base, [Link](base)) for base in set(seq)]
5 [('c', 15), ('g', 10), ('t', 11), ('a', 10)]

Les sets permettent aussi l’évaluation d’union ou d’intersection mathématiques en conjonction avec les opérateurs,
respectivement | et & :

1 >>> liste1 = [3, 3, 5, 1, 3, 4, 1, 1, 4, 4]


2 >>> liste2 = [3, 0, 5, 3, 3, 1, 1, 1, 2, 2]
3 >>> set(liste1) | set(liste2)
4 {0, 1, 2, 3, 4, 5}
5 >>> set(liste1) & set(liste2)
6 {1, 3, 5}

Notez qu’il existe les méthodes .union() et .intersection permettant de réaliser ces opérations d’union et
d’intersection :

1 >>> set1 = {1, 3, 4, 5}


2 >>> set2 = {0, 1, 2, 3, 5}
3 >>> [Link](set2)
4 {0, 1, 2, 3, 4, 5}
5 >>> [Link](set2)
6 {1, 3, 5}

L’instruction [Link](set2) renvoie sous la forme d’un nouveau set les éléments de set1 qui ne sont
pas dans set2. Et inversement pour [Link](set1) :

1 >>> [Link](set2)
2 {4}
3 >>> [Link](set1)
4 {0, 2}

Enfin, deux autres méthodes sont très utiles :

150 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


14.5. Récapitulation des propriétés des conteneurs Chapitre 14. Conteneurs

1 >>> set1 = set(range(10))


2 >>> set2 = set(range(3, 7))
3 >>> set3 = set(range(15, 17))
4 >>> set1
5 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
6 >>> set2
7 {3, 4, 5, 6}
8 >>> set3
9 {16, 15}
10 >>> [Link](set1)
11 True
12 >>> [Link](set1)
13 True

La méthode .issubset() indique si un set est inclus dans un autre set. La méthode isdisjoint() indique si un
set est disjoint d’un autre set, c’est-à-dire, s’ils n’ont aucun élément en commun indiquant que leur intersection est nulle.
Il existe de nombreuses autres méthodes que nous n’abordons pas ici, mais qui peuvent être consultées sur la docu-
mentation officielle de Python 4 .

14.4.3 Frozensets
Les frozensets sont des sets non modifiables et hachables. Ainsi, un set peut contenir des frozensets mais pas l’inverse.
À quoi servent-ils ? Comme la différence entre tuple et liste, l’immutabilité des frozensets donne l’assurance de ne pas
pouvoir les modifier par erreur. Pour créer un frozenset on utilise la fonction interne frozenset(), qui prend en argument
un objet itérable et le convertit (opération de casting) :
1 >>> frozen1 = frozenset([3, 3, 5, 1, 3, 4, 1, 1, 4, 4])
2 >>> frozen2 = frozenset([3, 0, 5, 3, 3, 1, 1, 1, 2, 2])
3 >>> frozen1
4 frozenset({1, 3, 4, 5})
5 >>> frozen2
6 frozenset({0, 1, 2, 3, 5})
7 >>> [Link](5)
8 Traceback (most recent call last):
9 File "<stdin>", line 1, in <module>
10 AttributeError: 'frozenset' object has no attribute 'add'
11 >>> [Link](frozen2)
12 frozenset({0, 1, 2, 3, 4, 5})
13 >>> [Link](frozen2)
14 frozenset({1, 3, 5})

Les frozensets ne possèdent bien sûr pas les méthodes de modification des sets (.add(), .discard(), etc.) puisqu’ils
sont non modifiables. Par contre, ils possèdent toutes les méthodes de comparaisons de sets (.union(), .intersection
(), etc.).

Conseil
Pour aller plus loin sur les sets et les frozensets, voici deux articles sur les sites programiz 5 et towardsdatascience 6 .

14.5 Récapitulation des propriétés des conteneurs


Après ce tour d’horizon des différents conteneurs, voici des tableaux qui résument leurs propriétés. La mention « in
et len() » indique que l’on peut tester l’appartenance d’un élément à un conteneur avec l’opérateur in, et que l’on peut
connaître le nombre d’éléments du conteneur avec la fonction len(). Les mentions « index. » et « modif. » indiquent
respectivement « indexable » et « modifiable ».

14.5.1 Objets séquentiels

4. [Link]
5. [Link]
6. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 151


Chapitre 14. Conteneurs 14.6. Dictionnaires et sets de compréhension

Conteneur in et len() itérable ordonné index. modif. hachable


liste oui oui oui oui oui non
chaîne de caractères oui oui oui oui non oui
range oui oui oui oui non oui
tuple oui oui oui oui non oui∗

∗ s’il ne contient que des objets hachables

14.5.2 Objets de mapping

Conteneur in et len() itérable ordonné index. modif. hachable


dictionnaire oui oui sur les clés oui∗ non oui non

∗ à partir de Python 3.7 uniquement

14.5.3 Objets sets

Conteneur in et len() itérable ordonné index. modif. hachable


sets oui oui non non oui non
frozensets oui oui non non non oui

14.5.4 Types de base


Il est aussi intéressant de comparer ces propriétés avec celles des types numériques de base qui ne sont pas des
conteneurs.

Objet numérique in et len() itérable ordonné index. modif. hachable


entier non non non non non oui
float non non non non non oui
booléen non non non non non oui

14.5.5 Copie de conteneurs


Un dernier point qu’il peut être utile de mentionner concerne la copie de conteneurs. On avait vu dans le chapitre
12 Plus sur les listes que la copie de listes se fait par référence. Cela est un mécanisme général pour tous les types de
conteneurs, sauf pour les chaînes de caractères. Python Tutor nous permet de visualiser cela (Figure 14.2).
Ainsi, il faut toujours faire attention quand on fait une copie d’un conteneur modfiable (liste, dictionnaire, set, etc.).
On verra que Python se comporte de la même manière avec les objets arrays (chapitre 20 module Numpy) ou Dataframes
(chapitre 22 Module pandas), car on peut les considérer également comme des conteneurs.

14.6 Dictionnaires et sets de compréhension


Nous avons abordé les listes de compréhension dans le chapitre 12 Plus sur les listes. Il est également possible de
générer des dictionnaires de compréhension :
1 >>> dico = {"a": 10, "g": 10, "t": 11, "c": 15}
2 >>> [Link]()
3 dict_items([('a', 10), ('g', 10), ('t', 11), ('c', 15)])
4 >>> {key:val*2 for key, val in [Link]()}
5 {'a': 20, 'g': 20, 't': 22, 'c': 30}
6 >>>
7 >>> animaux = (("singe", 3), ("girafe", 4), ("rhinocéros", 2))
8 >>> {animal:nombre for animal, nombre in animaux}
9 {'singe': 3, 'girafe': 4, 'rhinocéros': 2}

152 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


14.7. Module collections Chapitre 14. Conteneurs

Figure 14.2 – Copie de conteneurs.

La méthode .items() vue dans le chapitre 8 Dictionnaires et tuples est particulièrement bien adaptée pour créer un
dictionnaire de compréhension, car elle permet d’itérer en même temps sur les clés et valeurs d’un dictionnaire.
Avec un dictionnaire de compréhension, on peut rapidement compter le nombre de chaque base dans une séquence
d’ADN :
1 >>> sequence = "atctcgatcgatcgcgctagctagctcgccatacgtacgactacgt"
2 >>> {base:[Link](base) for base in set(sequence)}
3 {'a': 10, 'g': 10, 't': 11, 'c': 15}

De manière générale, tout objet sur lequel on peut faire une double itération du type for var1, var2 in obj est
utilisable pour créer un dictionnaire de compréhension. Si vous souhaitez aller plus loin, vous pouvez consulter cet article
sur le site Datacamp 7 .
Il est également possible de générer des sets de compréhension sur le même modèle que les listes de compréhension :
1 >>> {i for i in range(10)}
2 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
3 >>> {i**2 for i in range(10)}
4 {0, 1, 64, 4, 36, 9, 16, 49, 81, 25}
5 >>>
6 >>> animaux = (("singe", 3), ("girafe", 4), ("rhinocéros", 2))
7 >>> {ani for ani, _ in animaux}
8 {'girafe', 'singe', 'rhinocéros'}

14.7 Module collections


Le module collections 8 contient d’autres types de conteneurs qui peuvent se révéler utiles, c’est une véritable mine
d’or ! Nous n’aborderons pas tous ces objets ici, mais nous pouvons citer tout de même certains d’entre eux si vous
souhaitez aller un peu plus loin :
• Les dictionnaires ordonnés 9 , qui se comportent comme les dictionnaires classiques, mais qui sont ordonnés, c’est-à-
dire que si on les affiche ou on itère dessus, l’ordre sera le même que celui utilisé pour sa création. Avant la version

7. [Link]
8. [Link]
9. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 153


Chapitre 14. Conteneurs 14.8. Exercices

3.6 de Python, ces dictionnaires ordonnés avaient un intérêt, car l’ordre des dictionnaires normaux était arbitraire.
Désormais, les dictionnaires normaux se comportent presque en tout point comme les dictionnaires ordonnés.
• Les defaultdicts 10 , qui génèrent des valeurs par défaut quand on demande une clé qui n’existe pas (cela évite que
Python génère une erreur).
• Les compteurs 11 , dont un exemple est présenté ci-dessous.
• Les namedtuples 12 , que nous évoquerons au chapitre 24 Avoir plus la classe avec les objets (en ligne).
L’objet [Link]() est particulièrement intéressant et simple à utiliser. Il crée des compteurs à partir
d’objets itérables, par exemple :
1 >>> import collections
2 >>> compo_seq = [Link]("aatctccgatcgatcgatcgatgatc")
3 >>> compo_seq
4 Counter({'a': 7, 't': 7, 'c': 7, 'g': 5})
5 >>> type(compo_seq)
6 <class '[Link]'>
7 >>> compo_seq["a"]
8 7
9 >>> compo_seq["n"]
10 0

Dans cet exemple, Python a automatiquement compté chaque caractère a, t, g et c de la chaîne de caractères passée
en argument. Cela crée un objet de type Counter qui se comporte ensuite comme un dictionnaire, à une exception près :
si on appelle une clé qui n’existe pas dans l’itérable initiale (comme le n ci-dessus), la valeur renvoyée est 0.

14.8 Exercices

Conseil
Pour ces exercices, créez des scripts puis exécutez-les dans un shell.

14.8.1 Séquence peptidique et dictionnaire


Les numéros d’acides aminés commencent rarement à 1 dans les fichiers PDB. Créez un dictionnaire où chaque clé
est un numéro de résidu de 3 à 9, et chaque valeur est un acide aminé de la séquence peptidique suivante : SEQPEPT.
Utilisez pour cela les fonctions dict() et zip().

14.8.2 Composition en acides aminés


En utilisant un set et la méthode .count() des chaînes de caractères, déterminez le nombre d’occurrences de chaque
acide aminé dans la séquence
AGWPSGGASAGLAILWGASAIMPGALW.

14.8.3 Mots de deux et trois lettres dans une séquence d’ADN


Créez une fonction compte_mots_2_lettres(), qui prend comme argument une séquence sous la forme d’une
chaîne de caractères et qui renvoie tous les mots de deux lettres qui existent dans la séquence sous la forme d’un
dictionnaire. Par exemple pour la séquence ACCTAGCCCTA, le dictionnaire renvoyée serait :
{'AC': 1, 'CC': 3, 'CT': 2, 'TA': 2, 'AG': 1, 'GC': 1}
Créez une nouvelle fonction compte_mots_3_lettres(), qui a un comportement similaire à compte_mots_2_lettres
(), mais avec des mots de trois lettres.
Utilisez ces fonctions pour afficher les mots de deux et trois lettres et leurs occurrences trouvés dans la séquence
d’ADN :
ACCTAGCCATGTAGAATCGCCTAGGCTTTAGCTAGCTCTAGCTAGCTG
Voici un exemple de sortie attendue :
10. [Link]
11. [Link]
12. [Link]

154 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


14.8. Exercices Chapitre 14. Conteneurs

Mots de 2 lettres
AC : 1
CC : 3
CT : 8
[...]
Mots de 3 lettres
ACC : 1
CCT : 2
CTA : 5
[...]

14.8.4 Mots de deux lettres dans la séquence du chromosome I de Saccharomyces cerevi-


siae
Créez une fonction lit_fasta() qui prend comme argument le nom d’un fichier FASTA sous la forme d’une chaîne
de caractères, lit la séquence dans le fichier FASTA et la renvoie sous la forme d’une chaîne de caractères. Inspirez-vous
d’un exercice similaire du chapitre 10 Plus sur les chaînes de caractères.
Utilisez cette fonction et la fonction compte_mots_2_lettres() de l’exercice précédent pour extraire les mots de
deux lettres et leurs occurrences dans la séquence du chromosome I de la levure du boulanger Saccharomyces cerevisiae
(fichier NC_001133.fna 13 ).
Le génome complet est fourni au format FASTA. Vous trouverez des explications sur ce format et des exemples de
code dans l’annexe A Quelques formats de données en biologie.

14.8.5 Mots de n lettres dans un fichier FASTA


Créez un script [Link] qui prend comme arguments le nom d’un fichier FASTA suivi d’un entier compris
entre 1 et 4. Ce script doit extraire du fichier FASTA tous les mots et leurs occurrences, en fonction du nombre de lettres
passé en option.
Utilisez pour ce script la fonction lit_fasta() de l’exercice précédent. Créez également la fonction compte_mots_n_lettres
() qui prend comme argument une séquence sous la forme d’une chaîne de caractères et le nombre de lettres des mots
sous la forme d’un entier.
Testez ce script avec :
• la séquence du chromosome I de la levure du boulanger Saccharomyces cerevisiae (fichier NC_001133.fna 14 ) ;
• le génome de la bactérie Escherichia coli (fichier NC_000913.fna 15 ).
Les deux fichiers sont au format FASTA.
Cette méthode vous paraît-elle efficace sur un génome assez gros comme celui d’Escherichia coli ?

14.8.6 Atomes carbone alpha d’un fichier PDB


Téléchargez le fichier [Link] 16 , qui correspond à la structure tridimensionnelle de la protéine barstar 17 sur le site
de la Protein Data Bank (PDB).
Créez la fonction trouve_calpha(), qui prend en argument le nom d’un fichier PDB (sous la forme d’une chaîne
de caractères), qui sélectionne uniquement les lignes contenant des carbones alpha et qui les renvoie sous la forme d’une
liste de dictionnaires. Chaque dictionnaire contient quatre clés :
• le numéro du résidu (resid) avec une valeur entière,
• la coordonnée atomique x (x) avec une valeur float,
• la coordonnée atomique y (y) avec une valeur float,
• la coordonnée atomique z (z) avec une valeur float.
Utilisez la fonction trouve_calpha() pour afficher à l’écran le nombre total de carbones alpha de la barstar ainsi
que les coordonnées atomiques des carbones alpha des deux premiers résidus (acides aminés).

Conseil
13. [Link]
14. [Link]
15. [Link]
16. [Link]
17. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 155


Chapitre 14. Conteneurs 14.8. Exercices

Vous trouverez des explications sur le format PDB et des exemples de code pour lire ce type de fichier en Python
dans l’annexe A Quelques formats de données en biologie.

14.8.7 Barycentre d’une protéine (exercice +++)


Téléchargez le fichier [Link] 18 qui correspond à la structure tridimensionnelle de la protéine barstar 19 sur le site
de la Protein Data Bank (PDB).
Un carbone alpha est présent dans chaque résidu (acide aminé) d’une protéine. On peut obtenir une bonne approxi-
mation du barycentre d’une protéine en calculant le barycentre de ses carbones alpha.
Le barycentre G de coordonnées (Gx , Gy , Gz ) est obtenu à partir des n carbones alpha (CA) de coordonnées (CAx ,
CAy , CAz ) avec :

1 n
Gx = ∑ CAi,x
n i=1

1 n
Gy = ∑ CAi,y
n i=1

1 n
Gz = ∑ CAi,z
n i=1
Créez une fonction calcule_barycentre(), qui prend comme argument une liste de dictionnaires dont les clés
(resid, x, y et z) sont celles de l’exercice précédent et qui renvoie les coordonnées du barycentre sous la forme d’une
liste de floats.
Utilisez la fonction trouve_calpha() de l’exercice précédent et la fonction
calcule_barycentre() pour afficher, avec deux chiffres significatifs, les coordonnées du barycentre des carbones alpha
de la barstar.

14.8.8 Kinases et protéines humaines


Nous avons extrait de la base de données de protéines UniProt la liste des protéines humaines (dans le fichier
human_proteins.txt 20 ) et la liste des kinases (dans le fichier kinases_proteins.txt 21 ), qui sont une famille de
protéines enzymatiques 22 impliquées dans la phosphorylation d’autres protéines.
Chaque fichier contient un identifiant de protéine par ligne. Voici un exemple pour le fichier human_proteins.txt :
A0A087X1C5
A0A0B4J2F0
A0A0B4J2F2
A0A0C5B5G6
A0A0K2S4Q6
A0A0U1RRE5
A0A1B0GTW7
A0AV02
A0AV96
[...]

L’objectif de cet exercice est de déterminer quelles sont les protéines humaines qui sont des kinases. Chaque liste de
protéines contenant plusieurs milliers d’éléments, il n’est pas possible de la faire à la main. Vous aller utiliser Python et
les sets pour cela.
1. Créez un script compare_proteins.py.
2. Dans ce script, créez une fonction read_protein_file() qui prend en argument le nom d’un fichier de protéines
sous la forme d’une chaîne de caractères et qui renvoie un set contenant la liste des identifiants des protéines
contenues dans le fichier passé en argument.
18. [Link]
19. [Link]
20. [Link]
21. [Link]
22. [Link]

156 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


14.8. Exercices Chapitre 14. Conteneurs

3. Affichez ensuite le nombre de protéines listées dans chaque fichier.


4. En utilisant uniquement des opérations sur les sets, déterminez et affichez :
• le nombre de protéines humaines qui sont des kinases ;
• le nombre de protéines humaines qui ne sont pas des kinases ;
• le nombre de kinases qui ne sont pas des protéines humaines.

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 157


CHAPITRE 15

Création de modules

15.1 Pourquoi créer ses propres modules ?


Dans le chapitre 9 Modules, nous avons découvert quelques modules Python comme random, math, etc. Nous avons
vu par ailleurs dans les chapitres 10 Fonctions et 13 Plus sur les fonctions que les fonctions sont utiles pour réutiliser
une fraction de code plusieurs fois au sein d’un même programme, sans avoir à dupliquer ce code. On peut imaginer
qu’une fonction utile pourrait être judicieusement réutilisée dans un autre programme Python. C’est justement l’intérêt
de créer un module. On y regroupe un ensemble de fonctions que l’on peut être amené à utiliser souvent. En général, les
modules sont regroupés autour d’un thème précis. Par exemple, on pourrait concevoir un module d’analyse de séquences
biologiques ou encore de gestion de fichiers PDB.

15.2 Création d’un module


En Python, la création d’un module est très simple. Il suffit d’écrire un ensemble de fonctions (et éventuellement de
constantes) dans un fichier, puis d’enregistrer ce dernier avec une extension .py (comme n’importe quel script Python).
À titre d’exemple, nous allons créer un module simple que nous enregistrerons sous le nom [Link] :
1 """Module inutile qui affiche des messages."""
2
3 DATE = "2024-01-05"
4
5
6 def bonjour(nom):
7 """Dit Bonjour."""
8 return f"Bonjour {nom}"
9
10
11 def ciao(nom):
12 """Dit Ciao."""
13 return f"Ciao {nom}"
14
15
16 def hello(nom):
17 """Dit Hello."""
18 return f"Hello {nom}"

Les chaînes de caractères entre triple guillemets en tête du module et en tête de chaque fonction sont facultatives
mais elles jouent néanmoins un rôle essentiel dans la documentation du code.

158
15.3. Utilisation de son propre module Chapitre 15. Création de modules

Remarque
Une constante est, par définition, une variable dont la valeur n’est pas modifiée. Par convention, en Python, le nom
des constantes est écrit en majuscules (comme DATE dans notre exemple).

15.3 Utilisation de son propre module


Pour appeler une fonction ou une variable de ce module, il faut que le fichier [Link] soit dans le répertoire
courant (dans lequel on travaille) ou bien dans un répertoire listé par la variable d’environnement PYTHONPATH de votre
système d’exploitation. Ensuite, il suffit d’importer le module et toutes ses fonctions (et constantes) vous sont alors
accessibles.

Remarque
Avec Mac OS X et Linux, il faut taper la commande suivante depuis un shell Bash pour modifier la variable d’envi-
ronnement PYTHONPATH :
export PYTHONPATH=$PYTHONPATH:/chemin/vers/mon/super/module
Avec Windows, mais depuis un shell PowerShell, il faut taper la commande suivante :
$env:PYTHONPATH += ";C:\chemin\vers\mon\super\module"
Une fois cette manipulation effectuée, vous pouvez contrôler que le chemin vers le répertoire contenant vos modules
a bien été ajouté à la variable d’environnement PYTHONPATH :
• sous Mac OS X et Linux : echo $PYTHONPATH
• sous Windows : echo $env:PYTHONPATH

Le chargement du module se fait avec la commande import message. Notez que le fichier est bien enregistré avec
une extension .py, pourtant on ne la précise pas lorsqu’on importe le module. Ensuite, on peut utiliser les fonctions
comme avec un module classique :
1 >>> import message
2 >>> [Link]("Joe")
3 'Hello Joe'
4 >>> [Link]("Bill")
5 'Ciao Bill'
6 >>> [Link]("Monsieur")
7 'Bonjour Monsieur'
8 >>> [Link]
9 '2024-01-05'

Remarque
La première fois qu’un module est importé, Python crée un répertoire nommé __pycache__ contenant un fichier
avec une extension .pyc qui contient le bytecode 1 , c’est-à-dire le code précompilé du module.

15.4 Les docstrings


Lorsqu’on écrit un module, il est important de créer de la documentation pour expliquer ce que fait le module et
comment utiliser chaque fonction. Les chaînes de caractères entre triple guillemets, situées en début du module et de
chaque fonction, sont là pour cela : on les appelle docstrings (« chaînes de documentation » en français). Les docstrings
seront détaillées dans le chapitre 16 Bonnes pratiques en programmation Python.
Les docstrings permettent notamment de fournir de l’aide lorsqu’on invoque la commande help() :

1. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 159


Chapitre 15. Création de modules 15.5. Visibilité des fonctions dans un module

1 >>> import message


2 >>> help(message)
3
4 Help on module message:
5
6 NAME
7 message - Module inutile qui affiche des messages.
8
9 FUNCTIONS
10 bonjour(nom)
11 Dit Bonjour.
12
13 ciao(nom)
14 Dit Ciao.
15
16 hello(nom)
17 Dit Hello.
18
19 DATA
20 DATE = '2024-01-05'
21
22 FILE
23 /home/pierre/[Link]

Remarque
Pour quitter l’aide, pressez la touche Q.

Vous remarquez que Python a généré automatiquement cette page d’aide, tout comme il est capable de le faire pour
les modules internes à Python (random, math, etc.) et ce grâce aux docstrings. Notez que l’on peut aussi appeler l’aide
pour une seule fonction :
1 >>> help([Link])
2
3 Help on function ciao in module message:
4
5 ciao(nom)
6 Dit Ciao.

En résumé, les docstrings sont destinés aux utilisateurs du module. Leur but est différent des commentaires qui, eux,
sont destinés à celui qui lit le code (pour en comprendre les subtilités). Une bonne docstring de fonction doit contenir
tout ce dont un utilisateur a besoin pour utiliser cette fonction. Une liste minimale et non exhaustive serait :
• ce que fait la fonction,
• ce qu’elle prend en argument,
• ce qu’elle renvoie.
Pour en savoir plus sur les docstrings et comment les écrire, nous vous recommandons de lire le chapitre 16 Bonnes
pratiques en programmation Python.

15.5 Visibilité des fonctions dans un module


La visibilité des fonctions au sein des modules suit des règles simples :
• Les fonctions dans un même module peuvent s’appeler les unes les autres.
• Les fonctions dans un module peuvent appeler des fonctions situées dans un autre module s’il a été préalablement
importé. Par exemple, si la commande import autremodule est utilisée dans un module, il est possible d’appeler
une fonction avec [Link]().
Toutes ces règles viennent de la manière dont Python gère les espaces de noms. De plus amples explications sont
données sur ce concept dans le chapitre 24 Avoir plus la classe avec les objets (en ligne).

160 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


15.6. Module ou script ? Chapitre 15. Création de modules

15.6 Module ou script ?


Vous avez remarqué que notre module message ne contient que des fonctions et une constante. Si on l’exécutait
comme un script classique, cela n’afficherait rien :
$ python [Link]
$

Cela s’explique par l’absence de programme principal, c’est-à-dire de lignes de code que l’interpréteur exécute lorsqu’on
lance le script.
À l’inverse, que se passe-t-il si on importe un script en tant que module alors qu’il contient un programme principal
avec des lignes de code ? Prenons par exemple le script [Link] suivant :
1 """Script de test."""
2
3
4 def bonjour(nom):
5 """Dit Bonjour."""
6 return f"Bonjour {nom}"
7
8
9 # Programme principal.
10 print(bonjour("Joe"))

Si on l’importe dans l’interpréteur, on obtient :


1 >>> import message2
2 Bonjour Joe

Ceci n’est pas le comportement voulu pour un module, car on n’attend pas d’affichage particulier lors de son charge-
ment. Par exemple la commande import math n’affiche rien dans l’interpréteur.
Afin de pouvoir utiliser un code Python en tant que module ou en tant que script, nous vous conseillons la structure
suivante :
1 """Script de test."""
2
3
4 def bonjour(nom):
5 """Dit Bonjour."""
6 return f"Bonjour {nom}"
7
8
9 if __name__ == "__main__":
10 print(bonjour("Joe"))

À la ligne 9, l’instruction if __name__ == "__main__": indique à Python :


• Si le programme [Link] est exécuté en tant que script dans un shell, le résultat du test if sera alors True
et le bloc d’instructions correspondant (ligne 10) sera exécuté :
$ python [Link]
Bonjour Joe

• Si le programme [Link] est importé en tant que module, le résultat du test if sera alors False et le bloc
d’instructions correspondant ne sera pas exécuté :
1 >>> import message2
2 >>>

Ce comportement est possible grâce à la gestion des espaces de noms par Python (pour plus de détail, consultez le
chapitre 24 Avoir plus la classe avec les objets (en ligne)). Au delà de la commodité de pouvoir utiliser votre script en
tant que programme ou en tant que module, cela présente l’avantage de signaler clairement où se situe le programme
principal quand on lit le code.

Conseil

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 161


Chapitre 15. Création de modules 15.7. Exercice

Ainsi, au lieu d’ajouter le commentaire :


# Programme principal.
comme nous vous l’avions suggéré dans les chapitres 10 Fonctions et 13 Plus sur les fonctions, nous vous recomman-
dons désormais d’utiliser la ligne :
if __name__ == "__main__":

15.7 Exercice

Conseil
Pour cet exercice, créez un script puis exécutez-le dans un shell.

15.7.1 Module ADN


Dans le script [Link], construisez un module qui va contenir les fonctions et constantes suivantes.
• Fonction lit_fasta() : prend en argument un nom de fichier sous forme d’une chaîne de caractères et renvoie la
séquence d’ADN lue dans le fichier sous forme d’une chaîne de caractères.
• Fonction seq_alea() : prend en argument une taille de séquence sous forme d’un entier et renvoie une séquence
aléatoire d’ADN de la taille correspondante sous forme d’une chaîne de caractères.
• Fonction comp_inv() : prend en argument une séquence d’ADN sous forme d’une chaîne de caractères et renvoie
la séquence complémentaire inverse (aussi sous forme d’une chaîne de caractères).
• Fonction prop_gc() : prend en argument une séquence d’ADN sous forme d’une chaîne de caractères et renvoie
la proportion en GC de la séquence sous forme d’un float. Nous vous rappelons que la proportion de GC s’obtient
comme la somme des bases Guanine (G) et Cytosine (C), divisée par le nombre total de bases (A, T, C, G).
• Constante BASE_COMP : dictionnaire qui contient la complémentarité des bases d’ADN (A→T, T→C, G→C et C→G).
Ce dictionnaire sera utilisé par la fonction comp_inv().
À la fin de votre script, proposez des exemples d’utilisation des fonctions que vous aurez créées. Ces exemples
d’utilisation ne devront pas être exécutés lorsque le script est chargé comme un module.

Conseil
• Dans cet exercice, on supposera que toutes les séquences sont manipulées comme des chaînes de caractères en
majuscules.
• Pour les fonctions seq_alea() et comp_inv(), n’hésitez pas à jeter un œil aux exercices correspondants dans le
chapitre 12 Plus sur les listes.
• Voici un exemple de fichier FASTA [Link] 2 pour tester la fonction lit_fasta().

2. [Link]

162 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


CHAPITRE 16

Bonnes pratiques en programmation Python

Comme vous l’avez constaté dans tous les chapitres précédents, la syntaxe de Python est très permissive. Afin
d’uniformiser l’écriture de code en Python, la communauté des développeurs Python recommande un certain nombre de
règles afin qu’un code soit lisible. Lisible par quelqu’un d’autre, mais également, et surtout, par soi-même. Essayez de
relire un code que vous avez écrit « rapidement » il y a un mois, six mois ou un an. Si le code ne fait que quelques lignes,
il se peut que vous vous y retrouviez, mais s’il fait plusieurs dizaines, voire centaines de lignes, vous serez perdus.
Dans ce contexte, le créateur de Python, Guido van Rossum, part d’un constat simple : « code is read much more
often than it is written » (« le code est plus souvent lu qu’écrit »). Avec l’expérience, vous vous rendrez compte que cela
est parfaitement vrai. Alors, plus de temps à perdre, voyons en quoi consistent ces bonnes pratiques.
Plusieurs choses sont nécessaires pour écrire un code lisible : la syntaxe, l’organisation du code, le découpage en
fonctions (et possiblement en classes, que nous verrons dans le chapitre 23 Avoir la classe avec les objets), mais souvent,
aussi, le bon sens. Pour cela, les « PEP » peuvent nous aider.

Définition
Afin d’améliorer le langage Python, la communauté qui développe Python publie régulièrement des Python Enhance-
ment Proposal 1 (PEP), suivi d’un numéro. Il s’agit de propositions concrètes pour améliorer le code, ajouter de nouvelles
fonctionnalités, mais aussi des recommandations sur la manière d’utiliser Python, bien écrire du code, etc.

On va aborder dans ce chapitre sans doute la plus célèbre des PEP, à savoir la PEP 8, qui est incontournable lorsque
l’on veut écrire du code Python correctement.

Définition
On parle de code pythonique lorsque ce dernier respecte les règles d’écriture définies par la communauté Python,
mais aussi les règles d’usage du langage.

1. [Link]

163
Chapitre 16. Bonnes pratiques en programmation Python 16.1. De la bonne syntaxe avec la PEP 8

16.1 De la bonne syntaxe avec la PEP 8


La PEP 8 Style Guide for Python Code 2 est une des plus anciennes PEP (les numéros sont croissants avec le temps).
Elle consiste en un nombre important de recommandations sur la syntaxe de Python. Il est vivement recommandé de lire
la PEP 8 en entier au moins une fois pour avoir une bonne vue d’ensemble. On ne présentera ici qu’un rapide résumé de
cette PEP 8.

16.1.1 Indentation
On a vu que l’indentation est obligatoire en Python pour séparer les blocs d’instructions. Cela vient d’un constat
simple : l’indentation améliore la lisibilité d’un code. La PEP 8 recommande d’utiliser quatre espaces pour chaque niveau
d’indentation. Nous vous recommandons de suivre impérativement cette règle.

Attention
Afin de toujours utiliser cette règle des quatre espaces pour l’indentation, il est essentiel de régler correctement votre
éditeur de texte. Consultez pour cela l’annexe Installation de Python disponible en ligne 3 . Avant d’écrire la moindre ligne
de code, faites en sorte que lorsque vous pressez la touche tabulation, cela ajoute quatre espaces (et non pas un caractère
tabulation).

16.1.2 Importation des modules


Comme on l’a vu dans le chapitre 9 Modules, le chargement d’un module est réalisé avec l’instruction import module
plutôt qu’avec from module import *.
Si on souhaite ensuite utiliser une fonction d’un module, la première syntaxe conduit à [Link](), ce qui
rend explicite la provenance de la fonction. Avec la seconde syntaxe, il faudrait écrire fonction(), ce qui peut :
• mener à un conflit si une de vos fonctions a le même nom ;
• rendre difficile la recherche de documentation si on ne sait pas d’où vient la fonction, notamment si plusieurs
modules sont chargés avec l’instruction
from module import *
Par ailleurs, la première syntaxe définit un « espace de noms » spécifique au module (voir le chapitre 24 Avoir plus
la classe avec les objets (en ligne)).
Dans un script Python, on importe un module par ligne. D’abord les modules internes (classés par ordre alphabétique),
c’est-à-dire les modules de base de Python, puis les modules externes (ceux que vous avez installés en plus), et enfin, les
modules que vous avez créés.
Si le nom du module est trop long, on peut utiliser un alias. L’instruction from est tolérée si vous n’importez que
quelques fonctions clairement identifiées.
En résumé :
1 import module_interne_1
2 import module_interne_2
3 from module_interne_3 import fonction_spécifique
4
5 import module_externe_1
6 import module_externe_2_qui_a_un_nom_long as mod2
7
8 import module_cree_par_vous

16.1.3 Règles de nommage


Les noms de variables, de fonctions et de modules doivent être de la forme :
1 ma_variable
2 fonction_test_27()
3 mon_module

2. [Link]
3. [Link]

164 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


16.1. De la bonne syntaxe avec la PEP 8 Chapitre 16. Bonnes pratiques en programmation Python

c’est-à-dire en minuscules avec un caractère « souligné » (« tiret du bas », ou underscore en anglais) pour séparer les
différents « mots » dans le nom.
Les constantes sont écrites en majuscules :
1 MA_CONSTANTE
2 VITESSE_LUMIERE

Les noms de classes (voir le chapitre 23 Avoir la classe avec les objets) et les exceptions (voir le chapitre 26 Remarques
complémentaires (en ligne)) sont de la forme :
1 MaClasse
2 MyException

Remarque
• Le style recommandé pour nommer les variables et les fonctions en Python est appelé snake_case. Il est différent
du CamelCase utilisé pour les noms des classes et des exceptions.
• La variable _ est habituellement employée pour stocker des valeurs qui ne seront pas utilisées par la suite. Par
exemple, dans le cas d’une affectation multiple, on peut utiliser _ pour stocker une valeur qui ne nous intéresse pas
(voir chapitre 14 Conteneurs).

Pensez à donner à vos variables des noms qui ont du sens. Évitez autant que possible les a1, a2, i, truc, toto…
Les noms de variables à un caractère sont néanmoins autorisés pour les indices dans les boucles :
1 >>> ma_liste = [1, 3, 5, 7, 9, 11]
2 >>> for i in range(len(ma_liste)):
3 ... print(ma_liste[i])

Bien sûr, une écriture plus « pythonique » de l’exemple précédent permet de se débarrasser de l’indice i :
1 >>> ma_liste = [1, 3, 5, 7, 9, 11]
2 >>> for entier in ma_liste:
3 ... print(entier)

Enfin, des noms de variable à une lettre peuvent être utilisés lorsque cela a un sens mathématique (par exemple, les
noms x, y et z évoquent des coordonnées cartésiennes).

16.1.4 Gestion des espaces


La PEP 8 recommande d’entourer les opérateurs (+, -, /, *, ==, !=, >=, not, in, and, or…) d’un espace avant et
d’un espace après. Par exemple :
1 # Code recommandé :
2 ma_variable = 3 + 7
3 mon_texte = "souris"
4 mon_texte == ma_variable
5 # Code non recommandé :
6 ma_variable=3+7
7 mon_texte="souris"
8 mon_texte== ma_variable

Il n’y a, par contre, pas d’espace à l’intérieur de crochets, d’accolades et de parenthèses :


1 # Code recommandé :
2 ma_liste[1]
3 mon_dico{"clé"}
4 ma_fonction(argument)
5 # Code non recommandé :
6 ma_liste[ 1 ]
7 mon_dico{"clé" }
8 ma_fonction( argument )

Ni juste avant la parenthèse ouvrante d’une fonction ou le crochet ouvrant d’une liste ou d’un dictionnaire :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 165


Chapitre 16. Bonnes pratiques en programmation Python 16.1. De la bonne syntaxe avec la PEP 8

1 # Code recommandé :
2 ma_liste[1]
3 mon_dico{"clé"}
4 ma_fonction(argument)
5 # Code non recommandé :
6 ma_liste [1]
7 mon_dico {"clé"}
8 ma_fonction (argument)

On met un espace après les caractères : et , (mais pas avant) :


1 # Code recommandé :
2 ma_liste = [1, 2, 3]
3 mon_dico = {"clé1": "valeur1", "clé2": "valeur2"}
4 ma_fonction(argument1, argument2)
5 # Code non recommandé :
6 ma_liste = [1 , 2 ,3]
7 mon_dico = {"clé1" : "valeur1", "clé2":"valeur2"}
8 ma_fonction(argument1 ,argument2)

Par contre, pour les tranches de listes, on ne met pas d’espace autour du :
1 ma_liste = [1, 3, 5, 7, 9, 1]
2 # Code recommandé :
3 ma_liste[1:3]
4 ma_liste[1:4:2]
5 ma_liste[::2]
6 # Code non recommandé :
7 ma_liste[1 : 3]
8 ma_liste[1: 4:2 ]
9 ma_liste[ : :2]

Enfin, on n’ajoute pas plusieurs espaces autour du = ou des autres opérateurs pour faire joli :
1 # Code recommandé :
2 x1 = 1
3 x2 = 3
4 x_old = 5
5 # Code non recommandé :
6 x1 = 1
7 x2 = 3
8 x_old = 5

16.1.5 Longueur de ligne


Une ligne de code ne doit pas dépasser 79 caractères, pour des raisons tant historiques que de lisibilité.
On a déjà vu dans le chapitre 1 Introduction que le caractère \ permet de couper des lignes trop longues. Par exemple :
1 >>> ma_variable = 3
2 >>> if ma_variable > 1 and ma_variable < 10 \
3 ... and ma_variable % 2 == 1 and ma_variable % 3 == 0:
4 ... print(f"ma variable vaut {ma_variable}")
5 ...
6 ma variable vaut 3

À l’intérieur de parenthèses, on peut revenir à la ligne sans utiliser le caractère \. C’est particulièrement utile pour
préciser les arguments d’une fonction ou d’une méthode, lors de sa création ou lors de son utilisation :
1 >>> def ma_fonction(argument_1, argument_2,
2 ... argument_3, argument_4):
3 ... return argument_1 + argument_2
4 ...
5 >>> ma_fonction("texte très long", "tigre",
6 ... "singe", "souris")
7 'texte très longtigre'

Les parenthèses sont également très pratiques, pour répartir sur plusieurs lignes une chaîne de caractères qui sera

166 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


16.1. De la bonne syntaxe avec la PEP 8 Chapitre 16. Bonnes pratiques en programmation Python

ensuite affichée sur une seule ligne :


1 >>> print("ATGCGTACAGTATCGATAAC"
2 ... "ATGACTGCTACGATCGGATA"
3 ... "CGGGTAACGCCATGTACATT")
4 ATGCGTACAGTATCGATAACATGACTGCTACGATCGGATACGGGTAACGCCATGTACATT

Notez qu’il n’y a pas d’opérateur + pour concaténer les trois chaînes de caractères, et que celles-ci ne sont pas séparées
par des virgules. À partir du moment où elles sont entre parenthèses, Python les concatène automatiquement.
On peut aussi utiliser les parenthèses pour évaluer un expression trop longue :
1 >>> ma_variable = 3
2 >>> if (ma_variable > 1 and ma_variable < 10
3 ... and ma_variable % 2 == 1 and ma_variable % 3 == 0):
4 ... print(f"ma variable vaut {ma_variable}")
5 ...
6 ma variable vaut 3

Remarque
Les parenthèses sont aussi très utiles lorsqu’on a besoin d’enchaîner des méthodes les unes à la suite des autres. Cette
technique du method chaining a été introduite dans le chapitre 11 Plus sur les chaînes de caractères et sera très utilisée
dans le chapitre 22 Module Pandas.

Enfin, il est possible de créer des listes ou des dictionnaires sur plusieurs lignes, en sautant une ligne après une virgule :
1 >>> ma_liste = [1, 2, 3,
2 ... 4, 5, 6,
3 ... 7, 8, 9]
4 >>> mon_dico = {"clé1": 13,
5 ... "clé2": 42,
6 ... "clé3": -10}

16.1.6 Lignes vides


Dans un script, les lignes vides sont utiles pour séparer visuellement les différentes parties du code.
Il est recommandé de laisser deux lignes vides avant la définition d’une fonction ou d’une classe, et de laisser une
seule ligne vide avant la définition d’une méthode (dans une classe).
On peut aussi laisser une ligne vide dans le corps d’une fonction pour séparer les sections logiques de la fonction,
mais cela est à utiliser avec parcimonie.

16.1.7 Commentaires
Les commentaires débutent toujours par le symbole # suivi d’un espace. Ils fournissent des explications sur l’utilité
du code et permettent de comprendre son fonctionnement.
Les commentaires sont sur le même niveau d’indentation que le code qu’ils commentent. Les commentaires sont
constitués de phrases complètes, avec une majuscule au début (sauf si le premier mot est une variable qui s’écrit sans
majuscule) et un point à la fin.
La PEP 8 recommande d’écrire les commentaires en anglais, sauf si vous êtes absolument certains que votre code ne
sera lu que par des francophones. Dans la mesure où vous allez souvent développer des programmes scientifiques, nous
vous conseillons d’écrire vos commentaires en anglais.
Soyez également cohérent entre la langue utilisée pour les commentaires et la langue utilisée pour nommer les variables.
Pour un programme scientifique, les commentaires et les noms de variables sont en anglais. Ainsi ma_liste deviendra
my_list et ma_fonction deviendra my_function (par exemple).
Les commentaires qui suivent le code sur la même ligne sont à éviter le plus possible et doivent être séparés du code
par au moins deux espaces :
1 var_x = number / total * 100 # My useful comment.

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 167


Chapitre 16. Bonnes pratiques en programmation Python 16.2. Les docstrings et la PEP 257

Remarque
La PEP 8 ne fournit pas de recommandation 4 quant à l’usage de guillemets simples ou de guillemets doubles pour
déclarer une chaîne de caractères.
1 >>> var_1 = "Ma chaîne de caractères"
2 >>> var_1
3 'Ma chaîne de caractères'
4 >>> var_2 = 'Ma chaîne de caractères'
5 >>> var_2
6 'Ma chaîne de caractères'
7 >>> var_1 == var_2
8 True

Vous constatez dans l’exemple ci-dessus que, pour Python, les guillemets simples et doubles sont équivalents. Nous
vous conseillons cependant d’utiliser les guillemets doubles car ceux-ci sont, de notre point de vue, plus lisibles.

16.2 Les docstrings et la PEP 257


Les docstrings, que l’on pourrait traduire par « chaînes de documentation » en français, sont un élément essentiel
des programmes Python, comme on l’a vu au chapitre 15 Création de modules. À nouveau, les développeurs de Python
ont émis des recommandations dans la PEP 8, et plus exhaustivement dans la PEP 257 5 , sur la manière de rédiger
correctement les docstrings. En voici un résumé succinct.

16.2.1 Les principales règles


De manière générale, écrivez des docstrings pour les modules, les fonctions, les classes et les méthodes que vous
développez.
Lorsque l’explication est courte et compacte, comme dans certaines fonctions ou méthodes simples, utilisez des
docstrings d’une ligne :
1 """Docstring simple d'une ligne se finissant par un point."""

Lorsque vous avez besoin de décrire plus en détail un module, une fonction, une classe ou une méthode, utilisez une
docstring sur plusieurs lignes :
1 """Docstring de plusieurs lignes, la première ligne est un résumé.
2
3 Après avoir sauté une ligne, on décrit les détails de cette docstring.
4 On termine la docstring avec les triples guillemets
5 sur la ligne suivante.
6 """

Remarque
La PEP 257 recommande d’écrire des docstrings avec trois doubles guillemets, c’est-à-dire :
"""Ceci est une docstring recommandée."""
mais pas :
'''Ceci n'est pas une docstring recommandée.'''

Comme indiqué dans le chapitre 15 Création de modules, n’oubliez pas que les docstrings sont destinées aux utilisateurs
des modules, fonctions, méthodes et classes que vous avez développés. Les éléments essentiels pour les fonctions et les
méthodes sont :
1. ce que fait la fonction ou la méthode,
2. ce qu’elle prend en argument,
3. ce qu’elle renvoie.
4. [Link]
5. [Link]

168 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


16.3. Outils de contrôle qualité du code Chapitre 16. Bonnes pratiques en programmation Python

Pour les modules et les classes, on ajoute également des informations générales sur leur fonctionnement.
Pour autant, la PEP 257 ne dit pas explicitement comment organiser les docstrings pour les fonctions et les méthodes.
Pour répondre à ce besoin, deux solutions ont émergées :
• la solution Google avec le Google Style Python Docstrings 6 ,
• la solution NumPy avec le NumPy Style Python Docstrings 7 . NumPy est un module complémentaire à Python,
très utilisé en analyse de données et dont on parlera dans le chapitre 20.

16.2.2 Un exemple concret


On illustre ici la solution de docstrings NumPy pour des raisons de goût personnel. Sentez-vous libre d’explorer la
proposition de Google.
Voici un exemple pour une fonction qui prend en argument deux entiers et qui renvoie leur produit :
1 def multiplie_nombres(nombre1, nombre2):
2 """Multiplication de deux nombres entiers.
3
4 Cette fonction ne sert pas à grand chose.
5
6 Parameters
7 ----------
8 nombre1 : int
9 Le premier nombre entier.
10 nombre2 : int
11 Le second nombre entier,
12 très important pour cette fonction.
13
14 Returns
15 -------
16 int
17 Le produit des deux nombres.
18 """
19 return nombre1 * nombre2

• Lignes 6 et 7. La section Parameters précise les paramètres de la fonction. Les tirets sur la ligne 7 soulignent le
nom de la section pour la rendre visible.
• Lignes 8 et 9. On indique le nom et le type du paramètre, séparés par le caractère deux-points. Le type n’est pas
obligatoire. En dessous, on indique une description du paramètre en question. La description est indentée.
• Lignes 10 à 12. Même chose pour le second paramètre. La description du paramètre peut s’étaler sur plusieurs
lignes.
• Lignes 14 et 15. La section Returns indique ce qui est renvoyé par la fonction (le cas échéant).
• Lignes 16 et 17. La mention du type renvoyé est obligatoire. En dessous, on indique une description de ce qui est
renvoyé par la fonction. Cette description est aussi indentée.

Attention
L’être humain a une fâcheuse tendance à la procrastination (le fameux « Bah je le ferai demain…») et écrire de la
documentation peut être un sérieux motif de procrastination. Soyez vigilant sur ce point, et rédigez vos docstrings au
moment où vous écrivez vos modules, fonctions, classes ou méthodes. Passer une journée (voire plusieurs) à écrire les
docstrings d’un gros projet est particulièrement pénible. Croyez-nous !

16.3 Outils de contrôle qualité du code


Pour évaluer la qualité d’un code Python, c’est-à-dire sa conformité avec les recommandations de la PEP 8 et de la
PEP 257, on peut utiliser les outils pycodestyle, pydocstyle et pylint.
Ces outils ne sont fournis dans l’installation de base de Python et doivent être installés sur votre machine. Avec la
distribution Miniconda, cette étape d’installation se résume à une ligne de commande :

6. [Link]
7. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 169


Chapitre 16. Bonnes pratiques en programmation Python 16.3. Outils de contrôle qualité du code

$ conda install -c conda-forge pycodestyle pydocstyle pylint

Définition
Les outils pycodestyle, pydocstyle et pylint sont des linters, c’est-à-dire des programmes qui vont chercher les
sources potentielles d’erreurs dans un code informatique. Ces erreurs peuvent être des erreurs de style (PEP 8 et 257) ou
des erreurs logiques (manipulation d’une variable, chargement de module).

Voici le contenu du script script_quality_not_ok.py 8 que nous allons analyser par la suite :
1 """Un script de multiplication.
2 """
3
4 import os
5
6 def Multiplie_nombres(nombre1,nombre2 ):
7 """Multiplication de deux nombres entiers
8 Cette fonction ne sert pas à grand chose.
9
10 Parameters
11 ----------
12 nombre1 : int
13 Le premier nombre entier.
14 nombre2 : int
15 Le second nombre entier.
16 Très utile.
17
18 Returns
19 -------
20 int
21 Le produit des deux nombres.
22
23 """
24 return nombre1 *nombre2
25
26
27 if __name__ == "__main__":
28 print(f"2 x 3 = {Multiplie_nombres(2, 3)}")
29 print (f"4 x 5 = {Multiplie_nombres(4, 5)}")

Ce script est d’ailleurs parfaitement fonctionnel :


$ python script_quality_not_ok.py
2 x 3 = 6
4 x 5 = 20

On va tout d’abord vérifier la conformité avec la PEP 8 grâce à l’outil pycodestyle :


$ pycodestyle script_quality_not_ok.py
script_quality_not_ok.py:6:1: E302 expected 2 blank lines, found 1
script_quality_not_ok.py:6:30: E231 missing whitespace after ','
script_quality_not_ok.py:6:38: E202 whitespace before ')'
script_quality_not_ok.py:26:21: E225 missing whitespace around operator
script_quality_not_ok.py:31:10: E211 whitespace before '('

• Ligne 2. Le bloc script_quality_not_ok.py:6:1: désigne le nom du script (script_quality_not_ok.


py), le numéro de la ligne (6) et le numéro de la colonne (1) où se trouve la non-conformité avec la PEP 8.
Ensuite, pycodestyle fournit un code et un message explicatif. Ici, il faut deux lignes vides avant la fonction
Multiplie_nombres().
• Ligne 3. Il manque un espace après la virgule qui sépare les arguments nombre1 et nombre2 dans la définition de
la fonction Multiplie_nombres() à la ligne 6 (colonne 30) du script.

8. [Link]

170 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


16.4. Outil de formatage automatique du code Chapitre 16. Bonnes pratiques en programmation Python

• Ligne 4. Il y un espace de trop après le second argument nombre2 dans la définition de la fonction Multiplie_nombres
() à la ligne 6 (colonne 38) du script.
• Ligne 5. Il manque un espace après l’opérateur * à la ligne 26 (colonne 21) du script.
• Ligne 6. Il y a un espace de trop entre print et ( à la ligne 31 (colonne 10) du script.
Assez curieusement, pycodestyle n’a pas détecté que le nom de la fonction Multiplie_nombres() ne respecte
pas la convention de nommage (pas de majuscule).
Ensuite, l’outil pydocstyle va vérifier la conformité avec la PEP 257 et s’intéresser particulièrement aux docstrings :
$ pydocstyle script_quality_not_ok.py
script_quality_not_ok.py:1 at module level:
D200: One-line docstring should fit on one line with quotes (found 2)
script_quality_not_ok.py:7 in public function `Multiplie_nombres`:
D205: 1 blank line required between summary line and description (found 0)
script_quality_not_ok.py:7 in public function `Multiplie_nombres`:
D400: First line should end with a period (not 's')

• Lignes 2 et 3. pydocstyle indique que la docstring à la ligne 1 du script est sur deux lignes, alors qu’elle devrait
être sur une seule ligne.
• Lignes 4 et 5. Dans la docstring de la fonction Multiplie_nombres() (ligne 7 du script), il manque une ligne
vide entre la ligne résumé et la description plus complète.
• Lignes 6 et 7. Dans la docstring de la fonction Multiplie_nombres() (ligne 7 du script), il manque un point
à la fin de la première ligne.
Les outils pycodestyle et pydocstyle vont simplement vérifier la conformité aux PEP 8 et 257. L’outil pylint
va lui aussi vérifier une partie de ces règles mais il va également essayer de comprendre le contexte du code et proposer
des éléments d’amélioration. Par exemple :
$ pylint script_quality_not_ok.py
************* Module script_quality_not_ok
script_quality_not_ok.py:6:0: C0103: Function name "Multiplie_nombres"
doesn't conform to snake_case naming style (invalid-name)
script_quality_not_ok.py:4:0: W0611: Unused import os (unused-import)

------------------------------------------------------------------
Your code has been rated at 6.67/10

• Lignes 3 et 4. pylint indique que nom de la fonction Multiplie_nombres() ne respecte pas la convention
PEP 8 (ligne 6 du script).
• Ligne 5. Le module os est chargé mais pas utilisé (ligne 4 du script).
• Ligne 8. pylint produit également une note sur 10. Ne soyez pas surpris si cette note est très basse (voire
négative) la première fois que vous analysez votre script avec pylint. Cet outil fournit de nombreuses suggestions
d’amélioration et la note attribuée à votre script devrait rapidement augmenter. Pour autant, la note de 10 est
parfois difficile à obtenir. Ne soyez pas trop exigeant.
Une version améliorée du script précédent est disponible en ligne 9 .

16.4 Outil de formatage automatique du code


Se souvenir de toutes les règles PEP 8 est fastidieux. Il existe des outils pour formater automatiquement le code
Python pour qu’il soit conforme à la PEP 8. L’outil le plus connu est black.
Cet outil n’est pas fourni dans l’installation de base de Python et doit être installé sur votre machine. Avec la
distribution Miniconda, cette étape d’installation se résume à une ligne de commande :
$ conda install -c conda-forge black

Voici un exemple d’utilisation :


$ black script_quality_not_ok.py
reformatted script_quality_not_ok.py

All done!
1 file reformatted.

9. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 171


Chapitre 16. Bonnes pratiques en programmation Python 16.5. Organisation du code

Le script script_quality_not_ok.py a été modifié pour être conforme à la PEP 8, ce qu’on peut vérifier avec
pycodestyle :

$ pycodestyle script_quality_not_ok.py

qui ne renvoie aucune erreur.

black peut modifier votre code de manière significative. Il est donc recommandé de l’utiliser avec l’option --diff
au préalable pour afficher les modifications apportées. Par exemple, avec le programme script_quality_not_ok.py
qui n’aurait pas été modifié :

$ black --diff script_quality_not_ok.py


--- script_quality_not_ok.py 2024-02-05 12:07:04.851491+00:00
+++ script_quality_not_ok.py 2024-02-05 12:07:10.418009+00:00
@@ -1,11 +1,12 @@
"""Un script de multiplication.
"""

import os

-def Multiplie_nombres(nombre1,nombre2 ):
+
+def Multiplie_nombres(nombre1, nombre2):
[...]

Conseil

black est très pratique. N’hésitez pas à l’utiliser pour formater automatiquement votre code.

Attention

• black ne fait qu’une entorse à la PEP 8 : il autorise des longueurs de lignes jusqu’à 88 caractères. Si vous souhaitez
respecter strictement la PEP 8, utilisez l’option --line-length 79.
• black se limite à la PEP 8. Il ne vérifie pas la conformité avec la PEP 257 ni la qualité du code (imports inutiles,
etc.). Utilisez toujours pydocstyle et pylint en complément.

16.5 Organisation du code

Il est important de toujours structurer son code de la même manière. Ainsi, on sait tout de suite où trouver l’information
et un autre programmeur pourra s’y retrouver. Voici un exemple de code avec les différents éléments dans le bon ordre :

172 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


16.6. Conseils sur la conception d’un script Chapitre 16. Bonnes pratiques en programmation Python

1 """Docstring d'une ligne décrivant brièvement ce que fait le programme.


2
3 Usage:
4 ======
5 python nom_de_ce_super_script.py argument1 argument2
6
7 argument1: un entier signifiant un truc
8 argument2: une chaîne de caractères décrivant un bidule
9 """
10
11 __authors__ = ("Johny B Good", "Hubert de la Pâte Feuilletée")
12 __contact__ = ("johny@[Link]", "hub@[Link]")
13 __copyright__ = "MIT"
14 __date__ = "2030-01-01"
15 __version__ = "1.2.3"
16
17 import module_interne
18 import module_interne_2 as mod2
19
20 import module_externe
21
22 import my_module
23
24 UNE_CONSTANTE = valeur
25
26
27 def une_fonction_complexe(arg1, arg2, arg3):
28 """Résumé de la docstring décrivant la fonction.
29
30 Description détaillée.
31 """
32 [...]
33 return une_chose
34
35
36 def une_fonction_simple(arg1, arg2):
37 """Docstring d'une ligne décrivant la fonction."""
38 [...]
39 return autre_chose
40
41
42 if __name__ == "__main__":
43 # Ici débute le programme principal.
44 [...]

• Lignes 1 à 9. Cette docstring décrit globalement le script. Cette docstring (ainsi que les autres) seront visibles si
on importe le script en tant que module, puis en invoquant la commande help() (voir chapitre 15 Création de
modules).
• Lignes 11 à 15. On définit ici un certain nombre de variables avec des doubles underscores donnant quelques
informations sur la version du script, les auteurs, etc. Il s’agit de métadonnées que la commande help() pourra
afficher. Ces métadonnées sont utiles lorsque le code est distribué à la communauté.
• Lignes 17 à 22. Importation des modules. D’abord les modules internes à Python (fournis en standard), puis les
modules externes (ceux qu’il faut installer en plus), puis les modules créés localement. Un module par ligne.
• Ligne 24. Définition des constantes. Le nom des constantes est en majuscule.
• Lignes 27 à 39. Définition des fonctions. Avant chaque fonction, on laisse deux lignes vides.
• Lignes 42 à 44. On écrit le programme principal. Le test ligne 42 n’est vrai que si le script est utilisé en tant que
programme.

16.6 Conseils sur la conception d’un script


Voici quelques conseils pour vous aider à concevoir un script Python.
• Réfléchissez avec un papier, un crayon… et un cerveau (voire même plusieurs) ! Reformulez avec vos propres mots
les consignes qui vous ont été données. Dessinez des schémas si cela vous aide.
• Découpez en fonctions chaque élément de votre programme. Vous pourrez ainsi tester chaque élément indépen-

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 173


Chapitre 16. Bonnes pratiques en programmation Python 16.7. Pour terminer : la PEP 20

damment du reste. Pensez à écrire les docstrings en même temps que vous écrivez vos fonctions.
• Documentez-vous. L’algorithme dont vous avez besoin existe-t-il déjà dans un autre module ? De quels outils
mathématiques avez-vous besoin dans votre algorithme ?
• Quand l’algorithme est complexe, commentez votre code pour expliquer votre raisonnement. Utiliser des fonctions
(ou méthodes) encore plus petites peut aussi être une solution.
• Utilisez des noms de variables explicites, qui signifient quelque chose. En lisant votre code, on doit comprendre
ce que vous faites. Choisir des noms de variables pertinents permet aussi de réduire les commentaires.
• Quand vous construisez une structure de données complexe (par exemple une liste de dictionnaires contenant
d’autres objets), documentez l’organisation de cette structure de données avec un exemple simple.
• Si vous créez ou manipulez une entité cohérente avec des propriétés propres, essayez de construire une classe.
Reportez-vous, pour cela, au chapitre 23 Avoir la classe avec les objets.
• Testez votre code sur un petit jeu de données, pour comprendre rapidement ce qui se passe et corriger d’éven-
tuelles erreurs. Par exemple, une séquence d’ADN de 1 000 bases est plus facile à manipuler que le génome humain
(3 × 109 bases) !
• Lorsque votre programme « plante », lisez le message d’erreur. Python tente de vous expliquer ce qui ne va pas.
Le numéro de la ligne qui pose problème est aussi indiqué.
• Discutez avec des gens. Faites tester votre programme par d’autres. Les instructions d’utilisation sont-elles
claires ?
• Enfin, si vous distribuez votre code :
— Rédigez une documentation claire.
— Testez votre programme (jetez un œil aux tests unitaires 10 ).
— Précisez une licence d’utilisation (voir le site Choose an open source license 11 ).

16.7 Pour terminer : la PEP 20


La PEP 20 est une sorte de réflexion philosophique avec des phrases simples qui devraient guider tout programmeur.
Comme les développeurs de Python ne manque pas d’humour, celle-ci est accessible sous la forme d’un « œuf de Pâques
» (easter egg, en anglais) ou encore « fonctionnalité cachée d’un programme » en important un module nommé this :
1 >>> import this
2 The Zen of Python, by Tim Peters
3
4 Beautiful is better than ugly.
5 Explicit is better than implicit.
6 Simple is better than complex.
7 Complex is better than complicated.
8 Flat is better than nested.
9 Sparse is better than dense.
10 Readability counts.
11 Special cases aren't special enough to break the rules.
12 Although practicality beats purity.
13 Errors should never pass silently.
14 Unless explicitly silenced.
15 In the face of ambiguity, refuse the temptation to guess.
16 There should be one-- and preferably only one --obvious way to do it.
17 Although that way may not be obvious at first unless you're Dutch.
18 Now is better than never.
19 Although never is often better than *right* now.
20 If the implementation is hard to explain, it's a bad idea.
21 If the implementation is easy to explain, it may be a good idea.
22 Namespaces are one honking great idea -- let's do more of those!

Et si l’aventure et les easter eggs vous plaisent, testez également la commande


1 >>> import antigravity

Il vous faudra un navigateur et une connexion internet.

10. [Link]
11. [Link]

174 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


16.7. Pour terminer : la PEP 20 Chapitre 16. Bonnes pratiques en programmation Python

Pour aller plus loin


• L’article Python Code Quality : Tools & Best Practices 12 du site Real Python est une ressource intéressante pour
explorer plus en détail la notion de qualité pour un code Python. De nombreux linters y sont présentés.
• Les articles Assimilez les bonnes pratiques de la PEP 8 13 du site OpenClassrooms et Structuring Python Programs 14
du site Real Python rappellent les règles d’écriture et les bonnes pratiques vues dans ce chapitre.

12. [Link]
13. [Link]
14. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 175


CHAPITRE 17

Expressions régulières et parsing

Le module re permet d’utiliser des expressions régulières avec Python. Les expressions régulières sont aussi appelées
en anglais regular expressions, ou en plus court regex. Dans la suite de ce chapitre, nous utiliserons souvent le mot regex
pour désigner une expression régulière. Les expressions régulières sont puissantes et incontournables en bioinformatique,
surtout lorsque vous souhaitez récupérer des informations dans de gros fichiers.
Cette action de recherche de données dans un fichier est appelée généralement parsing (qui signifie littéralement «
analyse syntaxique »). Le parsing fait partie du travail quotidien du bioinformaticien, il est sans arrêt en train de « fouiller
» dans des fichiers pour en extraire des informations d’intérêt, par exemple récupérer les coordonnées 3D des atomes
d’une protéine dans un fichier PDB, ou encore extraire les gènes d’un fichier GenBank.
Dans ce chapitre, nous ne ferons que quelques rappels sur les expressions régulières. Pour une documentation plus
complète, référez-vous à la page d’aide des expressions régulières 1 sur le site officiel de Python.

17.1 Définition et syntaxe


Une expression régulière est une suite de caractères qui a pour but de décrire un fragment de texte. Cette suite de
caractères est encore appelée motif (en anglais, pattern), qui est constitué de deux types de caractères :
• les caractères dits normaux ;
• les métacaractères ayant une signification particulière, par exemple le caractère ^ signifie début de ligne, et non
pas le caractère « chapeau » littéral.
Avant de présenter les regex en Python, nous allons faire un petit détour par Unix. En effet, certains programmes,
comme egrep, sed ou encore awk, savent interpréter les expressions régulières. Tous ces programmes fonctionnent
généralement selon le schéma suivant :
• le programme lit un fichier ligne par ligne ;
• pour chaque ligne lue, si l’expression régulière passée en argument est retrouvée dans la ligne, alors le programme
effectue une action.
Par exemple, pour le programme egrep :
$ egrep "^DEF" herp_virus.gbk
DEFINITION Human herpesvirus 2, complete genome.

Ici, egrep affiche toutes les lignes du fichier du virus de l’herpès (herp_virus.gbk) dans lesquelles la regex ^DEF
(c’est-à-dire le mot DEF en début de ligne) est retrouvée.
1. [Link]

176
17.1. Définition et syntaxe Chapitre 17. Expressions régulières et parsing

Remarque
Il est intéressant de faire un point sur le vocabulaire utilisé en anglais et en français. En général, on utilise le verbe
to match pour indiquer qu’une regex « a fonctionné ». Bien qu’il n’y ait pas de traduction littérale en français, on peut
utiliser les verbes « retrouver » ou « correspondre ». Par exemple, on pourra traduire l’expression « The regex matches
the line » par « La regex est retrouvée dans la ligne » ou encore « La regex correspond dans la ligne ».

Après avoir introduit le vocabulaire des regex, voici quelques éléments de syntaxe des métacaractères :
^ Début de chaîne de caractères ou de ligne.
Exemple : la regex ^ATG est retrouvée dans la chaîne de caractères ATGCGT mais pas dans la chaîne CCATGTT.
$ Fin de chaîne de caractères ou de ligne.
Exemple : la regex ATG$ est retrouvée dans la chaîne de caractères TGCATG mais pas dans la chaîne CCATGTT.
. N’importe quel caractère (mais un caractère quand même).
Exemple : la regex A.G est retrouvée dans ATG, AtG, A4G, mais aussi dans A-G ou dans A G.
[ABC] Le caractère A ou B ou C (un seul caractère).
Exemple : la regex T[ABC]G est retrouvée dans TAG, TBG ou TCG, mais pas dans TG.
[A-Z] N’importe quelle lettre majuscule.
Exemple : la regex C[A-Z]T est retrouvée dans CAT, CBT, CCT…
[a-z] N’importe quelle lettre minuscule.
[0-9] N’importe quel chiffre.
[A-Za-z0-9] N’importe quel caractère alphanumérique.
[^AB] N’importe quel caractère sauf A et B.
Exemple : la regex CG[^AB]T est retrouvée dans CG9T, CGCT… mais pas dans CGAT ni dans CGBT.
\ Caractère d’échappement (pour protéger certains caractères).
Exemple : la regex \+ désigne le caractère + littéral. La regex A\.G est retrouvée dans A.G et non pas dans A suivi
de n’importe quel caractère, suivi de G.
* 0 à n fois le caractère précédent ou l’expression entre parenthèses précédente.
Exemple : la regex A(CG)*T est retrouvée dans AT, ACGT, ACGCGT…
+ 1 à n fois le caractère précédent ou l’expression entre parenthèses précédente.
Exemple : la regex A(CG)+T est retrouvée dans ACGT, ACGCGT… mais pas dans AT.
? 0 à 1 fois le caractère précédent ou l’expression entre parenthèses précédente.
Exemple : la regex A(CG)?T est retrouvée dans AT ou ACGT.
{n} n fois le caractère précédent ou l’expression entre parenthèses précédente.
Exemple : la regex A(CG){2}T est retrouvée dans ACGCGT mais pas dans ACGT, ACGCGCGT ou ACGCG.
{n,m} n à m fois le caractère précédent ou l’expression entre parenthèses précédente.
Exemple : la regex A(C){2,4}T est retrouvée dans ACCT, ACCCT et ACCCCT mais pas dans ACT, ACCCCCT ou ACCC.
{n,} Au moins n fois le caractère précédent ou l’expression entre parenthèses précédente.
Exemple : la regex A(C){2,}T est retrouvée dans ACCT, ACCCT et ACCCCT mais pas à ACT ou ACCC.
{,m} Au plus m fois le caractère précédent ou l’expression entre parenthèses précédente.
Exemple : la regex A(C){,2}T est retrouvée dans AT, ACT et ACCT mais pas dans ACCCT ou ACC.
(CG|TT) Les chaînes de caractères CG ou TT.
Exemple : la regex A(CG|TT)C est retrouvée dans ACGC ou ATTC.
Enfin, il existe des caractères spéciaux qui sont bien commodes et qui peuvent être utilisés en tant que métacaractères :
\d remplace n’importe quel chiffre (d signifie digit), équivalent à [0-9].
\w remplace n’importe quel caractère alphanumérique et le caractère souligné (underscore) (w signifie word character),
équivalent à [0-9A-Za-z_].

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 177


Chapitre 17. Expressions régulières et parsing 17.2. Quelques ressources en ligne

\s remplace n’importe quel « espace blanc » (whitespace) (s signifie space), équivalent à [ \t\n\r\f]. La notion
d’espace blanc a été abordée dans le chapitre 11 Plus sur les chaînes de caractères. Les espaces blancs les plus
classiques sont l’espace , la tabulation \t, le retour à la ligne \n, mais il en existe d’autres comme \r et \f que nous
ne développerons pas ici. \s est très pratique pour détecter une combinaison d’espace(s) et/ou de tabulation(s).

Comme vous le constatez, les métacaractères sont nombreux et leur signification est parfois difficile à maîtriser. Faites
particulièrement attention aux métacaractères ., + et * qui, combinés ensemble, peuvent donner des résultats ambigus.

Attention
Il est important de savoir par ailleurs que les regex sont « avides » (greedy en anglais) lorsqu’on utilise les métaca-
ractères + et *. Cela signifie que la regex cherchera à « s’étendre » au maximum. Par exemple, si on utilise la regex A+
pour faire une recherche dans la chaîne TTTAAAAAAAAGC, tous les A de cette chaîne (huit en tout) seront concernés, bien
que AA, AAA, etc. « fonctionnent » également avec cette regex.

17.2 Quelques ressources en ligne


Nous vous conseillons de tester systématiquement vos expressions régulières sur des exemples simples. Pour vous
aider, nous vous recommandons plusieurs sites internet :
• RegexOne 2 : tutoriel en ligne très bien fait.
• RegExr 3 et ExtendsClass 4 : visualisent tous les endroits où une regex est retrouvée dans un texte.
• [Link] 5 : interface simple et efficace, dédiée à Python.
• [Link] 6 : documentation exhaustive sur les regex (il y a même une section sur Python).
N’hésitez pas à explorer ces sites avant de vous lancer dans les exercices ou dans l’écriture de vos propres regex !

17.3 Le module re
17.3.1 La fonction search()
Dans le module re, la fonction search() est incontournable. Elle permet de rechercher un motif, c’est-à-dire une
regex, au sein d’une chaîne de caractères avec une syntaxe de la forme search(motif, chaine). Si motif est retrouvé
dans chaine, Python renvoie un objet du type SRE_Match.
Sans entrer dans les détails propres au langage orienté objet, si on utilise un objet du type SRE_Match dans un test,
il sera considéré comme vrai. Par exemple, si on recherche le motif tigre dans la chaîne de caractères "girafe tigre
singe" :
1 >>> import re
2 >>> animaux = "girafe tigre singe"
3 >>> [Link]("tigre", animaux)
4 <_sre.SRE_Match object at 0x7fefdaefe2a0>
5 >>> if [Link]("tigre", animaux):
6 ... print("OK")
7 ...
8 OK

Attention
Le motif que vous utilisez comme premier argument de la fonction search() sera interprété en tant que regex. Ainsi,
^DEF correspondra au mot DEF en début de chaîne et pas au caractère littéral ^suivi du mot DEF.

2. [Link]
3. [Link]
4. [Link]
5. [Link]
6. [Link]

178 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


17.3. Le module re Chapitre 17. Expressions régulières et parsing

17.3.2 Les fonctions match() et fullmatch()


Il existe aussi la fonction match() dans le module re qui fonctionne sur le modèle de search(). La différence est
qu’elle renvoie un objet du type SRE_Match seulement lorsque la regex correspond au début de la chaîne de caractères
(à partir du premier caractère) :
1 >>> animaux = "girafe tigre singe"
2 >>> [Link]("tigre", animaux)
3 <_sre.SRE_Match object at 0x7fefdaefe718>
4 >>> [Link]("tigre", animaux)
5 >>>
6 >>> animaux = "tigre singe"
7 >>> [Link]("tigre", animaux)
8 <_sre.SRE_Match object; span=(0, 5), match='tigre'>
9 >>>

Il existe également la fonction fullmatch(), qui renvoie un objet du type SRE_Match si et seulement si l’expression
régulière correspond exactement à la chaîne de caractères.
1 >>> animaux = "tigre "
2 >>> [Link]("tigre", animaux)
3 >>> animaux = "tigre"
4 >>> [Link]("tigre", animaux)
5 <_sre.SRE_Match object; span=(0, 5), match='tigre'>

De manière générale, nous vous recommandons l’usage de la fonction search(). Si vous souhaitez avoir une cor-
respondance avec le début de la chaîne de caractères comme dans la fonction match(), vous pouvez toujours utiliser
l’accroche de début de ligne ^. Si vous voulez une correspondance exacte, comme dans la fonction fullmatch(), vous
pouvez utiliser les métacaractères ^ et $, par exemple ^tigre$.

17.3.3 Compilation d’expressions régulières


Lorsqu’on a besoin de tester la même expression régulière sur plusieurs milliers de chaînes de caractères, il est pratique
de compiler préalablement la regex à l’aide de la fonction compile(), qui renvoie un objet de type SRE_Pattern :
1 >>> regex = [Link]("^tigre")
2 >>> regex
3 <_sre.SRE_Pattern object at 0x7fefdafd0df0>

On peut alors utiliser directement cet objet avec la méthode .search() :


1 >>> animaux = "girafe tigre singe"
2 >>> [Link](animaux)
3 >>> animaux = "tigre singe"
4 >>> [Link](animaux)
5 <_sre.SRE_Match object at 0x7fefdaefe718>
6 >>> animaux = "singe tigre"
7 >>> [Link](animaux)

17.3.4 Groupes
L’intérêt de l’objet de type SRE_Match renvoyé par Python lorsqu’une regex trouve une correspondance dans une
chaîne de caractères est de pouvoir ensuite récupérer certaines zones précises :
1 >>> regex = [Link]("([0-9]+)\.([0-9]+)")

Dans cet exemple, on recherche un nombre décimal, c’est-à-dire une chaîne de caractères :
• qui débute par un ou plusieurs chiffres [0-9]+,
• suivi d’un point \. (le point a d’habitude une signification de métacaractère, donc il faut l’échapper avec \ pour
qu’il retrouve sa signification de point),
• et qui se termine encore par un ou plusieurs chiffres [0-9]+.
Les parenthèses dans la regex créent des groupes ([0-9]+ deux fois) qui seront récupérés ultérieurement par la
méthode .group().

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 179


Chapitre 17. Expressions régulières et parsing 17.3. Le module re

1 >>> resultat = [Link]("pi vaut 3.14")


2 >>> [Link](0)
3 '3.14'
4 >>> [Link](1)
5 '3'
6 >>> [Link](2)
7 '14'
8 >>> [Link]()
9 8
10 >>> [Link]()
11 12

La totalité de la correspondance est donnée par .group(0), le premier élément entre parenthèses est donné par
.group(1) et le second par .group(2).
Les méthodes .start() et .end() donnent respectivement la position de début et de fin de la zone qui correspond
à la regex. Notez que la méthode .search() ne renvoie que la première zone qui correspond à l’expression régulière,
même s’il en existe plusieurs :
1 >>> resultat = [Link]("pi vaut 3.14 et e vaut 2.72")
2 >>> [Link](0)
3 '3.14'

17.3.5 La méthode .findall()


Pour récupérer chaque zone dans la regex, s’il y en a plusieurs, vous pouvez utiliser la méthode .findall() qui
renvoie une liste des éléments en correspondance :
1 >>> regex = [Link]("[0-9]+\.[0-9]+")
2 >>> resultat = [Link]("pi vaut 3.14 et e vaut 2.72")
3 >>> resultat
4 ['3.14', '2.72']

L’utilisation des groupes entre parenthèses est également possible, ceux-ci sont alors renvoyés sous la forme de tuples :
1 >>> regex = [Link]("([0-9]+)\.([0-9]+)")
2 >>> resultat = [Link]("pi vaut 3.14 et e vaut 2.72")
3 >>> resultat
4 [('3', '14'), ('2', '72')]

17.3.6 La méthode .sub()


Enfin, la méthode .sub() permet d’effectuer des remplacements assez puissants. Par défaut, la méthode .sub(
chaine1, chaine2) remplace toutes les occurrences trouvées par l’expression régulière dans chaine2 par chaine1.
Si vous souhaitez ne remplacer que les n premières occurrences, utilisez l’argument count=n :
1 >>> regex = [Link]("[0-9]+\.[0-9]+")
2 >>> [Link]("quelque chose", "pi vaut 3.14 et e vaut 2.72")
3 'pi vaut quelque chose et e vaut quelque chose'
4 >>> [Link]("quelque chose", "pi vaut 3.14 et e vaut 2.72", count=1)
5 'pi vaut quelque chose et e vaut 2.72'

Encore plus puissant, il est possible d’utiliser dans le remplacement des groupes qui ont été « capturés » avec des
parenthèses :
1 >>> regex = [Link]("([0-9]+)\.([0-9]+)")
2 >>> phrase = "pi vaut 3.14 et e vaut 2.72"
3 >>> [Link]("approximativement \\1", phrase)
4 'pi vaut approximativement 3 et e vaut vaut approximativement 2'
5 >>> [Link]("approximativement \\1 (puis .\\2)",phrase)
6 'pi vaut approximativement 3 (puis .14) et e vaut approximativement 2 (puis .72)'

Si vous avez capturé des groupes, il suffit d’utiliser \\1, \\2 (etc.) pour utiliser les groupes correspondants dans la
chaîne de caractères substituée. On notera que la syntaxe générale pour récupérer des groupes dans les outils qui gèrent
les regex est \1, \2, etc. Toutefois, Python nous oblige à mettre un deuxième backslash car il y a ici deux niveaux : un

180 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


17.4. Exercices Chapitre 17. Expressions régulières et parsing

premier niveau Python où on veut mettre un backslash littéral (donc \\), puis un second niveau regex dans lequel on
veut retrouver \1. Si cela est confus, retenez seulement qu’il faut mettre un \\ devant le numéro de groupe.
Enfin, sachez que la réutilisation d’un groupe précédemment capturé est aussi utilisable lors d’une utilisation classique
de regex. Par exemple :
1 >>> [Link]("(pan)\\1", "bambi et panpan")
2 <_sre.SRE_Match object; span=(9, 15), match='panpan'>
3 >>> [Link]("(pan)\\1", "le pistolet a fait pan !")
4 >>>

Dans la regex (pan)\\1, on capture d’abord le groupe (pan) grâce aux parenthèses (il s’agit du groupe 1, puisque
c’est le premier jeu de parenthèses), immédiatement suivi du même groupe grâce au \\1. Dans cet exemple, on capture
donc le mot panpan (lignes 1 et 2). Si, par contre, on a une seule occurrence du mot pan, cette regex ne fonctionne
pas, ce qui est le cas ligne 3.
Bien sûr, si on avait eu un deuxième groupe, on aurait pu le réutiliser avec \\2, un troisième groupe avec \\3, etc.
Nous espérons vous avoir convaincu de la puissance du module re et des expressions régulières. Alors, plus de temps
à perdre, à vos regex !

17.4 Exercices

Conseil
Pour ces exercices, créez des scripts puis exécutez-les dans un shell.

17.4.1 Regex de base


Dans cet exercice, nous allons manipuler le fichier GenBank NC_001133.gbk 7 correspondant au chromosome I de la
levure Saccharomyces cerevisiae.
Créez un script regex_genbank.py :
• qui recherche le mot DEFINITION en début de ligne dans le fichier GenBank, puis affiche la ligne correspondante ;
• qui recherche tous les journaux (mot-clé JOURNAL) dans lesquels ont été publiés les travaux sur cette séquence,
puis affiche les lignes correspondantes.

Conseil
• Utilisez des regex pour trouver les lignes demandées.
• Des explications sur le format GenBank et des exemples de code sont fournies dans l’annexe A Quelques formats
de données en biologie.

17.4.2 Enzyme de restriction


Une enzyme de restriction est une protéine capable de couper une molécule d’ADN. Cette coupure se fait sur le site
de restriction de l’ADN qui correspond à une séquence particulière de nucléotides (bases).
Pour chacune des enzymes ci-dessous, déterminez les expressions régulières qui décrivent leurs sites de restriction. Le
symbole N correspond aux bases A, T, C ou G. W correspond à A ou T. Y correspond à C ou T. R correspond à A ou G.

Enzyme Site de restriction


HinFI GANTC
EcoRII CCWGG
BbvBI GGYRCC
BcoI CYCGRG
Psp5II RGGWCCY

7. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 181


Chapitre 17. Expressions régulières et parsing 17.4. Exercices

Enzyme Site de restriction


BbvAI GAANNNNTTC

17.4.3 Nettoyeur d’espaces


Le fichier cigale_fourmi.txt 8 contient la célèbre fable de Jean de la Fontaine. Malheureusement, la personne qui
l’a recopié a parfois mis plusieurs espaces au lieu d’un seul entre les mots.
Créez un script cigale_fourmi.py qui, grâce à une regex et à la fonction sub(), remplace plusieurs espaces par un
seul dans le texte ci-dessus. Le nouveau texte, ainsi nettoyé, sera enregistré dans un fichier cigale_fourmi_propre.txt.

17.4.4 Liste des protéines humaines


Téléchargez le fichier [Link] 9 qui contient le protéome humain, c’est-à-dire les séquences de
l’ensemble des protéines chez l’Homme. Ce fichier est au format FASTA.
On souhaite lister toutes ces protéines et les indexer avec un numéro croissant.
Créez un script liste_proteome.py qui :
• lit le fichier [Link] ;
• extrait, avec une regex, le numéro d’accession de la protéine de toutes les lignes de commentaires des séquences ;
• affiche le mot protein, suivi d’un numéro qui s’incrémente, suivi du numéro d’accession.
Voici un exemple de sortie attendue :
protein 00001 O95139
protein 00002 O75438
protein 00003 Q8N4C6
[...]
protein 20371 Q8IZJ1
protein 20372 Q9UKP6
protein 20373 Q96HZ7

Conseil
• Des explications sur le format FASTA et des exemples de code sont fournis dans l’annexe A Quelques formats de
données en biologie.
• La ligne de commentaire d’une séquence au format FASTA est de la forme
>sp|O95139|NDUB6_HUMAN NADH dehydrogenase [...]
Elle débute toujours pas le caractère >. Le numéro d’accession O95139 se situe entre le premier et le second symbole
| (symbole pipe). Attention, il faudra « échapper » ce symbole car il a une signification particulière dans une regex.
• Le numéro qui s’incrémente débutera à 1 et sera affiché sur 5 caractères, avec des 0 à sa gauche si nécessaires
(formatage {:05d}).

17.4.5 Le défi du dé-HTMLiseur (exercice +++)


Le format HTML permet d’afficher des pages web dans un navigateur. Il s’agit d’un langage à balise qui fonctionne
avec des balises ouvrantes <balise> et des balises fermantes </balise>.
Créez un script [Link] qui lit le fichier fichier_a_dehtmliser.html 10 au format HTML et qui renvoie
à l’écran tout le texte de ce fichier sans les balises HTML.
Nous vous conseillons tout d’abord d’ouvrir le fichier HTML dans un éditeur de texte et de bien l’observer. N’hésitez
pas à vous aider des sites mentionnés dans les ressources en ligne.

17.4.6 Nettoyeur de doublons (exercice +++)


Téléchargez le fichier breves_doublons.txt 11 qui contient des mots répétés deux fois. Par exemple :
8. [Link]
9. [Link]
10. [Link]
11. [Link]

182 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


17.4. Exercices Chapitre 17. Expressions régulières et parsing

Le cinéma est devenu parlant, la radio radio finira en images.


La sardine, c'est un petit petit poisson sans tête qui vit dans l'huile.
[...]

Écrivez un script ote_doublons.py qui lit le fichier breves_doublons.txt et qui supprime tous les doublons à
l’aide d’une regex. Le script affichera le nouveau texte à l’écran.

Conseil
Utilisez la méthode .sub().

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 183


CHAPITRE 18

Jupyter et ses notebooks

Les notebooks Jupyter sont des cahiers électroniques qui, dans le même document, peuvent rassembler du texte,
des images, des formules mathématiques, des tableaux, des graphiques et du code informatique exécutable. Ils sont
manipulables interactivement dans un navigateur web.
Initialement développés pour les langages de programmation Julia, Python et R (d’où le nom Jupyter), les notebooks
Jupyter supportent près de 40 langages différents.
La cellule est l’élément de base d’un notebook Jupyter. Elle peut contenir du texte formaté au format Markdown ou
du code informatique qui pourra être exécuté.
Voici un exemple de notebook Jupyter (figure 18.1) :
Ce notebook est constitué de cinq cellules : deux avec du texte en Markdown (la première et la dernière) et trois avec
du code Python (légèrement grisées).

18.1 Installation
Avec la distribution Miniconda, les notebooks Jupyter s’installent avec la commande :
$ conda install -c conda-forge -y jupyterlab

Pour être exact, la commande précédente installe un peu plus que les notebooks Jupyter, mais nous verrons cela par
la suite.

18.2 JupyterLab
En 2018, le consortium Jupyter a lancé JupyterLab, qui est un environnement complet de programmation et d’analyse
de données.
Pour obtenir cette interface, lancez la commande suivante depuis un shell :
$ jupyter lab

Une nouvelle page devrait s’ouvrir dans votre navigateur web et vous devriez obtenir une interface similaire à la figure
18.2, avec à gauche un navigateur de fichiers et à droite le « Launcher », qui permet de créer un nouveau notebook
Jupyter, de lancer un terminal ou d’éditer un fichier texte, un fichier Mardown, un script Python…
L’interface proposée par JupyterLab est très riche. On peut y organiser un notebook Jupyter, un éditeur de fichier
texte, un terminal… Les possibilités sont nombreuses et nous vous invitons à explorer cette interface par vous-même.

184
18.3. Création d’un notebook Chapitre 18. Jupyter et ses notebooks

Figure 18.1 – Exemple de notebook Jupyter. Les chiffres entourés désignent les différentes cellules.

18.3 Création d’un notebook


Pour créer un notebook, cliquez sur le bouton Python 3 situé dans la rubrique Notebook dans le Launcher (figure
18.3).
Le notebook fraîchement créé ne contient qu’une cellule vide.
La première chose à faire est de donner un nom à votre notebook. Pour cela, cliquer avec le bouton droit de la souris
sur [Link], en haut du notebook. Si le nom de votre notebook est [Link], alors le fichier [Link] sera créé
dans le répertoire depuis lequel vous avez lancé JupyterLab.

Remarque
L’extension .ipynb est l’extension de fichier des notebooks Jupyter.

Vous pouvez entrer des instructions Python dans la première cellule. Par exemple :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 185


Chapitre 18. Jupyter et ses notebooks 18.3. Création d’un notebook

Figure 18.2 – Interface de JupyterLab.

Figure 18.3 – Création d’un nouveau notebook.

Figure 18.4 – Nouveau notebook avec une cellule vide.

1 a = 2
2 b = 3
3 print(a+b)

186 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


18.3. Création d’un notebook Chapitre 18. Jupyter et ses notebooks

Pour exécuter le contenu de cette cellule, vous avez plusieurs possibilités :


• Cliquer sur le menu Run, puis Run Selected Cells.
• Cliquer sur le bouton � dans la barre de menu au dessus du notebook.
• Presser simultanément les touches Ctrl + Entrée.
Dans tous les cas, vous devriez obtenir un résultat similaire à la figure 18.5. La notation [1] à gauche de la cellule
indique qu’il s’agit de la première cellule de code qui a été exécutée.

Figure 18.5 – Exécution d’une première cellule.

Pour créer une nouvelle cellule, vous avez, ici encore, plusieurs possibilités :
• Cliquer sur l’icône + dans la barre de menu au dessus du notebook.
• Cliquer sur la 2e icône à partir de la droite (juste à côté de la poubelle), dans les icônes situées à l’intérieur de la
cellule, à droite.
Une nouvelle cellule vide devrait apparaître.
Vous pouvez également créer une nouvelle cellule, en positionnant votre curseur dans la première cellule, puis en
pressant simultanément les touches Alt + Entrée. Si vous utilisez cette combinaison de touches, vous remarquerez que
le numéro à gauche de la première cellule est passée de [1] à [2], car vous avez exécuté une nouvelle fois la première
cellule puis créé une nouvelle cellule.
Vous pouvez ainsi créer plusieurs cellules les unes à la suite des autres. Un objet créé dans une cellule antérieure sera
disponible dans les cellules suivantes. Par exemple, dans la figure 18.6, nous avons quatre cellules.

Figure 18.6 – Notebook avec plusieurs cellules de code Python.

Dans un notebook Jupyter, il est parfaitement possible de réexécuter une cellule précédente. Par exemple la première
cellule, qui porte désormais à sa gauche la numérotation [5] (voir figure 18.7).

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 187


Chapitre 18. Jupyter et ses notebooks 18.4. Le format Markdown

Figure 18.7 – Notebook avec une cellule ré-exécutée.

Attention
La possibilité d’exécuter les cellules d’un notebook Jupyter dans un ordre arbitraire peut prêter à confusion, notamment
si vous modifiez la même variable dans plusieurs cellules.
Nous vous recommandons de régulièrement relancer complètement l’exécution de toutes les cellules de votre notebook,
de la première à la dernière, en cliquant sur le menu Kernel puis Restart Kernel and Run All Cells et enfin de valider le
message Restart Kernel ? en cliquant sur le bouton Restart.

18.4 Le format Markdown


Dans le tout premier exemple (figure 18.1), nous avons vu qu’il était possible de mettre du texte au format Markdown
dans une cellule.
Il faut cependant indiquer à Jupyter que cette cellule est au format Markdown en cliquant sur Code, sous la barre de
menu au dessus du notebook, puis en choisissant Markdown.
Le format Markdown permet de rédiger du texte formaté (gras, italique, liens, titres, images, formules mathéma-
tiques…) avec quelques balises très simples. Voici un exemple dans un notebook Jupyter (figure 18.8 (A)) et le rendu
lorsque la cellule est exécutée (figure 18.8 (B)). Notez qu’une cellule Markdown est sur fond blanc (comme sur la figure
18.8 (B)).
Le format Markdown permet de rédiger du texte structuré rapidement et simplement. Ce cours est par exemple
complètement rédigé en Markdown. Nous vous conseillons d’explorer les possibilités du Markdown en consultant la page
Wikipédia 1 ou directement la page de référence 2 .

18.5 Des graphiques dans les notebooks


Un autre intérêt des notebooks Jupyter est de pouvoir y incorporer des graphiques réalisés avec la bibliothèque
matplotlib (que nous verrons prochainement).
Voici un exemple, d’un graphique qui sera présenté dans le chapitre 21 Module Matplotlib (figure 18.9).
L’instruction %matplotlib inline n’est pas nécessaire dans les versions récentes de JupyterLab. Mais avec d’an-
ciennes versions, vous pourriez en avoir besoin pour que les graphiques s’affichent dans le notebook.

1. [Link]
2. [Link]

188 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


18.6. Les magic commands Chapitre 18. Jupyter et ses notebooks

Remarque
Pour quitter l’interface JupyterLab, il y a plusieurs possibilités :
• Dans le menu en haut à gauche de l’interface, cliquer sur File, puis Shut Down, puis confirmer en cliquant sur le
bouton Shut Down.
• Une méthode plus radicale est de revenir sur le shell depuis lequel JupyterLab a été lancé, puis de presser deux fois
de suite la combinaison de touches Ctrl + C.

18.6 Les magic commands


La commande précédente (%matplotlib inline) est une magic command. Les magic commands 3 apportent des
fonctionnalités supplémentaires dans un notebook. Il en existe beaucoup, nous allons en aborder ici quelques unes.

Remarque
Dans cette rubrique, nous vous montrerons quelques exemples d’utilisation de magic commands exécutées dans un
notebook Jupyter.
1 Les cellules de code apparaitront de cette manière
2 dans un notebook Jupyter, avec des numéros de lignes à gauche.

Les résultats seront affichés de cette manière,


éventuellement sur plusieurs lignes.

18.6.1 %whos
La commande %whos liste tous les objets (variables, fonctions, modules…) utilisés dans un notebook.
Si une cellule précédente contenait le code :
1 a = 2
2 b = 3
3
4 def ma_fonction(x, y):
5 return x + y
6
7 resultat_1 = ma_fonction(a, 10)
8 resultat_2 = ma_fonction("Bonjour", "Jupyter")

alors l’exécution de :
1 %whos

renvoie :
Variable Type Data/Info
-----------------------------------
a int 2
b int 3
ma_fonction function <function ma_fonction at 0x7f219c2d04a0>
resultat_1 int 12
resultat_2 str BonjourJupyter

18.6.2 %history
La commande %history liste toutes les commandes Python lancées dans un notebook :
1 %history

3. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 189


Chapitre 18. Jupyter et ses notebooks 18.6. Les magic commands

a = 2
b = 3
print(a + b)
def ma_fonction(x, y):
return x + y
ma_fonction(a, 10)
ma_fonction("Bonjour", "Jupyter")
%whos
%history

18.6.3 %%time
La commande %%time (avec deux symboles %) va mesurer le temps d’exécution d’une cellule. C’est très utile pour
faire des tests de performance. On peut, par exemple, comparer les vitesses de parcours d’une liste avec une boucle for,
par les éléments ou par les indices des éléments.
Ainsi, cette cellule :
1 %%time
2 concentrations = [5.5, 7.2, 11.8, 13.6, 19.1, 21.7, 29.4]
3 somme_carres = 0.0
4 for conc in concentrations:
5 somme_carres += conc**2

renvoie :
CPU times: user 8 µs, sys: 2 µs, total: 10 µs
Wall time: 11.9 µs

et celle-ci :
1 %%time
2 concentrations = [5.5, 7.2, 11.8, 13.6, 19.1, 21.7, 29.4]
3 somme_carres = 0.0
4 for idx in range(len(concentrations)):
5 somme_carres += concentrations[idx]**2

renvoie :
CPU times: user 26 µs, sys: 5 µs, total: 31 µs
Wall time: 37.4 µs

Comme attendu, la première méthode (itération par les éléments) est plus rapide que la seconde (itération par les
indices des éléments). Les temps obtenus dépendent de la machine sur laquelle vous exécutez ces commandes. Mais, sur
une même machine, les résultats peuvent fluctuer d’une exécution à l’autre en fonction de l’activité de la machine. Ces
fluctuations seront d’autant plus importantes que le temps d’exécution est court.

18.6.4 %%timeit
Pour palier à ce problème, la magic command %%timeit va exécuter plusieurs fois la cellule et donner une estimation
du temps d’exécution moyen. Python détermine automatiquement le nombre d’itérations et le nombre de répétitions à
effectuer pour obtenir un temps global d’exécution raisonnable.
En reprenant l’exemple précédent, on obtient :
1 %%timeit
2 concentrations = [5.5, 7.2, 11.8, 13.6, 19.1, 21.7, 29.4]
3 somme_carres = 0.0
4 for conc in concentrations:
5 somme_carres += conc**2

492 ns ± 11.8 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

et

190 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


18.7. Lancement d’une commande Unix Chapitre 18. Jupyter et ses notebooks

1 %%timeit
2 concentrations = [5.5, 7.2, 11.8, 13.6, 19.1, 21.7, 29.4]
3 somme_carres = 0.0
4 for idx in range(len(concentrations)):
5 somme_carres += concentrations[idx]**2

606 ns ± 21.6 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

Ici, chaque cellule sera exécutée un million de fois sur sept répétitions, soit sept millions de fois au total. Comme
nous l’avions expliqué dans le chapitre 5 Boucles et comparaisons, itérer une liste sur ses éléments est la méthode la plus
efficace (et la plus élégante).

18.7 Lancement d’une commande Unix


Enfin, dans les environnements Linux ou Mac OS X, il est possible de lancer une commande Unix depuis un notebook
Jupyter. Il faut pour cela faire précéder la commande du symbole « ! ». Par exemple, la commande ls affiche le contenu
du répertoire courant :
1 !ls

[Link] [Link] [Link]


[Link] [Link]

Pour aller plus loin


Le lancement d’une commande Unix depuis un notebook Jupyter (en précédant cette commande de !) est très utile
pour réaliser de grosses analyses de données. Pour vous en rendre compte, explorez ce notebook 4 qui reproduit une analyse
complète de données de séquençage haut débit. Ces résultats ont donné lieu à la publication de l’article scientifique «
An open RNA-Seq data analysis pipeline tutorial with an example of reprocessing data from a recent Zika virus study 5
» (F1000 Research, 2016).

Conseil
Les notebooks Jupyter sont particulièrement adaptés à l’analyse de données en combinaison avec les modules mat-
plotlib et pandas, qui seront abordés dans les prochains chapitres.

4. [Link]
5. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 191


Chapitre 18. Jupyter et ses notebooks 18.7. Lancement d’une commande Unix

Figure 18.8 – Notebook avec : (A) une cellule au format Markdown et (B) le rendu après exécution.

192 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


18.7. Lancement d’une commande Unix Chapitre 18. Jupyter et ses notebooks

Figure 18.9 – Incorporation d’un graphique dans un notebook Jupyter.

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 193


CHAPITRE 19

Module Biopython

Nous allons aborder dans ce chapitre un module incontournable en bioinformatique. En effet, le module Biopython 1
permet de manipuler des données biologiques, comme des séquences (nucléiques et protéiques) ou des structures (fichiers
PDB), et d’interroger des bases de données comme PubMed. Le tutoriel 2 est particulièrement bien fait, n’hésitez pas à
le consulter.

19.1 Installation et convention


Contrairement aux autres modules vus précédemment, Biopython n’est pas fourni avec la distribution Python de base.
Avec la distribution Miniconda que nous vous conseillons d’utiliser (consultez pour cela la documentation en ligne 3 ),
vous pouvez rapidement l’installer avec la commande :
$ conda install -c conda-forge biopython

Dans ce chapitre, nous vous montrerons quelques exemples d’utilisation du module Biopython pour vous convaincre
de sa pertinence. Ces exemples seront exécutés dans un notebook Jupyter.
1 Les cellules de code apparaitront de cette manière
2 dans un notebook Jupyter, avec des numéros de lignes à gauche.

Les résultats seront affichés de cette manière,


éventuellement sur plusieurs lignes.

19.2 Chargement du module


On charge le module Biopython avec la commande :
1 import Bio

Attention
Le nom du module Biopython n’est pas biopython, mais Bio (avec un B majuscule).

1. [Link]
2. [Link]
3. [Link]

194
19.3. Manipulation de séquences Chapitre 19. Module Biopython

19.3 Manipulation de séquences


Voici quelques exemples de manipulation de séquences avec Biopython.

19.3.1 Définition d’une séquence


1 import Bio
2 from [Link] import Seq
3 ADN = Seq("ATATCGGCTATAGCATGC")
4 ADN

Seq('ATATCGGCTATAGCATGC')

• Ligne 1. Le module Biopython s’appelle Bio.


• Ligne 2. On charge la classe Seq du sous-module [Link].
• Ligne 3. La variable ADN est de type Seq, comme affiché dans le résultat.

19.3.2 Obtention de la séquence complémentaire et de la séquence complémentaire inverse


1 [Link]()

Seq('TATAGCCGATATCGTACG')

1 ADN.reverse_complement()

Seq('GCATGCTATAGCCGATAT')

19.3.3 Traduction en séquence protéique


1 [Link]()

Seq('ISAIAC')

Conseil
Dans l’annexe A Quelques formats de données en biologie, vous trouverez de nombreux exemples d’utilisation de
Biopython pour manipuler des données aux formats FASTA, GenBank et PDB.

19.4 Interrogation de la base de données PubMed


Le sous-module Entrez de Biopython permet d’utiliser les ressources du NCBI et notamment d’interroger la base
de données PubMed 4 . Nous allons par exemple utiliser PubMed pour chercher des articles scientifiques relatifs à la
transferrine (transferrin en anglais) :
1 from Bio import Entrez
2 [Link] = "votremail@[Link]"
3 req_esearch = [Link](db="pubmed", term="transferrin")
4 res_esearch = [Link](req_esearch)

• Ligne 1. On charge directement le sous-module Entrez.


• Ligne 2. Lors d’une requête sur le site du NCBI, il est important de définir correctement la variable [Link],
qui sera transmise au NCBI lors de la requête et qui pourra être utilisée pour vous contacter en cas de difficulté
avec le serveur.
4. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 195


Chapitre 19. Module Biopython 19.4. Interrogation de la base de données PubMed

• Ligne 3. On lance la requête (transferrin) sur le moteur de recherche pubmed. La requête est stockée dans la
variable req_esearch.
• Ligne 4. Le résultat est lu et stocké dans la variable res_esearch.
Sans être un vrai dictionnaire, la variable res_esearch en a cependant plusieurs propriétés. Voici ses clés :
1 res_esearch.keys()

dict_keys(['Count', 'RetMax', 'RetStart', 'IdList', 'TranslationSet',


'TranslationStack', 'QueryTranslation'])

La valeur associée à la clé IdList est une liste qui contient les identifiants (PMID) des articles scientifiques associés
à la requête (ici transferrin) :
1 res_esearch["IdList"]

['30411489', '30409795', '30405884', '30405827', '30402883', '30401570',


'30399508', '30397276', '30395963', '30394734', '30394728', '30394123',
'30393423', '30392910', '30392664', '30391706', '30391651', '30391537',
'30391296', '30390672']

1 len(res_esearch["IdList"])

20

Cette liste ne contient les identifiants que de 20 publications, alors que, si nous faisons cette même requête directement
sur le site de PubMed depuis un navigateur web, nous obtenons plus de 45 700 résultats.
En réalité, le nombre exact de publications (en janvier 2024) est connu :
1 res_esearch["Count"]

'45717'

Pour ne pas saturer les serveurs du NCBI, seulement 20 PMID sont renvoyés par défaut. Mais vous pouvez augmenter
cette limite en utilisant le paramètre retmax dans la fonction [Link]().
Nous pouvons maintenant récupérer des informations sur une publication précise en connaissant son PMID, par
exemple, l’article avec le PMID 22294463 5 , dont un aperçu est sur la figure 19.1.

Figure 19.1 – Aperçu de la publication Known and potential roles of transferrin in iron biology depuis le site PubMed.

Nous allons pour cela utiliser la fonction [Link]()


5. [Link]

196 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


19.4. Interrogation de la base de données PubMed Chapitre 19. Module Biopython

1 req_esummary = [Link](db="pubmed", id="22294463")


2 res_esummary = [Link](req_esummary)

La variable res_esummary n’est pas réellement une liste (son type exacte est [Link]),
mais elle est indexable (voir chapitre 14 Conteneurs). Cette pseudo-liste n’a qu’un seul élément, qui est lui-même un
dictionnaire dont voici les clés :
1 res_esummary[0].keys()

dict_keys(['Item', 'Id', 'PubDate', 'EPubDate', 'Source', 'AuthorList',


'LastAuthor', 'Title', 'Volume', 'Issue', 'Pages', 'LangList',
'NlmUniqueID', 'ISSN', 'ESSN', 'PubTypeList', 'RecordStatus', 'PubStatus',
'ArticleIds', 'DOI', 'History', 'References', 'HasAbstract', 'PmcRefCount',
'FullJournalName', 'ELocationID', 'SO'])

Nous pouvons alors facilement obtenir le titre, le DOI et la date de publication (PubDate) de cet article, ainsi que le
journal (Source) dans lequel il a été publié :
1 res_esummary[0]["Title"]

'Known and potential roles of transferrin in iron biology.'

1 res_esummary[0]["DOI"]

'10.1007/s10534-012-9520-3'

1 res_esummary[0]["PubDate"]

'2012 Aug'

1 res_esummary[0]["Source"]

'Biometals'

Enfin, pour récupérer le résumé de la publication précédente, nous allons utiliser la fonction [Link]() :
1 req_efetch = [Link](
2 db="pubmed", id="22294463",
3 rettype="abstract", retmode="text")
4 req_efetch.read()

'1. Biometals. 2012 Aug;25(4):677-86. doi: 10.1007/s10534-012-9520-3.


\n\nKnown and potential roles of transferrin in iron biology.\n\nBart
nikas TB(1).\n\nAuthor information:\n(1)Department of Pathology, Chil’
drens Hospital, Enders 1110, 300 Longwood \nAvenue, Boston, MA 02115
, USA. [Link]@[Link]\n\nTransferrin is an abund
ant serum metal-binding protein best known for its role \nin iron del
[...]

Le résultat n’est pas très lisible, car il apparait comme un seul bloc. Le caractère \n désigne un retour à la ligne.
L’instruction print() affichera le résultat de manière plus lisible :
1 req_efetch = [Link](
2 db="pubmed", id="22294463",
3 rettype="abstract", retmode="text")
4 print(req_efetch.read())

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 197


Chapitre 19. Module Biopython 19.5. Exercices

1. Biometals. 2012 Aug;25(4):677-86. doi: 10.1007/s10534-012-9520-3.

Known and potential roles of transferrin in iron biology.

Bartnikas TB(1).

Author information:
(1)Department of Pathology, ’Childrens Hospital, Enders 1110, 300 Longwood
Avenue, Boston, MA 02115, USA. [Link]@[Link]

Transferrin is an abundant serum metal-binding protein best known for its role
in iron delivery. The human disease congenital atransferrinemia and animal
models of this disease highlight the essential role of transferrin in
erythropoiesis and iron metabolism. Patients and mice deficient in transferrin
exhibit anemia and a paradoxical iron overload attributed to deficiency in
hepcidin, a peptide hormone synthesized largely by the liver that inhibits
dietary iron absorption and macrophage iron efflux. Studies of inherited human
disease and model organisms indicate that transferrin is an essential regulator
of hepcidin expression. In this paper, we review current literature on
transferrin deficiency and present our recent findings, including potential
overlaps between transferrin, iron and manganese in the regulation of hepcidin
expression.

DOI: 10.1007/s10534-012-9520-3
PMCID: PMC3595092
PMID: 22294463 [Indexed for MEDLINE]

Le résultat contient bien le résumé de la figure 19.1, mais aussi d’autres informations comme le titre, le DOI, la date
de publication…

19.5 Exercices

Conseil
Pour ces exercices, utilisez des notebooks Jupyter.

19.5.1 Pourcentage de GC de gènes de Plasmodium falciparum


Plasmodium falciparum (P. falciparum) est un des parasites responsables du paludisme chez les êtres humains. Le
fichier p_falciparum_500.fasta 6 contient 500 gènes du génome de P. falciparum.
Écrivez un code Python qui calcule le pourcentage de GC de chaque gène. Les valeurs seront stockées dans un
dictionnaire, avec comme clés les identifiants des gènes et comme valeurs le pourcentage de GC.
On rappelle que le pourcentage de GC d’une séquence est calculé avec la formule suivante :

nombre de bases G + nombre de bases C


pourcentage GC = × 100
longueur de la séquence
Affichez ensuite :
• Le nombre total de gènes.
• L’identifiant de la séquence qui a le pourcentage de GC le plus élevé, avec la valeur du pourcentage affichée avec
deux chiffres après la virgule.
• L’identifiant de la séquence qui a le pourcentage de GC le plus faible, avec la valeur du pourcentage affichée avec
deux chiffres après la virgule.

Conseil
Pour cet exercice, n’hésitez pas à consulter :
• Le chapitre 14 Conteneurs pour trier un dictionnaire.
• L’annexe A Quelques formats de données en biologie pour lire un fichier FASTA avec Biopython.
6. [Link]

198 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


19.5. Exercices Chapitre 19. Module Biopython

19.5.2 Années de publication des articles relatifs à la barstar


L’objectif de cet exercice est d’interroger automatiquement la base de données bibliographique PubMed pour déter-
miner le nombre d’articles relatifs à la protéine barstar publiés chaque année.
Vous utiliserez le module Biopython et le module matplotlib, qui sera vu un peu plus loin (les principales instructions
vous seront fournies).

[Link] Requête avec un mot-clé

Sur le site de PubMed 7 , cherchez combien d’articles scientifiques sont relatifs à la barstar.
Effectuez la même chose avec Python et la méthode [Link]() de Biopython.
Choisissez un des PMID renvoyé et vérifiez dans PubMed que l’article associé est bien à propos de la barstar. Pour
cela, indiquez le PMID choisi dans la barre de recherche de PubMed et cliquez sur Search. Attention, l’association n’est
pas toujours évidente. Cherchez éventuellement dans le résumé de l’article si besoin.
Est-ce que le nombre total d’articles trouvés est cohérent avec celui obtenu sur le site de PubMed ?

[Link] Récupération des informations d’une publication

Récupérez les informations de la publication dont le PMID est 29701945 8 . Vous utiliserez la méthode Entrez.
esummary().
Affichez le titre, le DOI, le nom du journal (Source) et la date de publication (PubDate) de cet article. Vérifiez que
cela correspond bien à ce que vous avez lu sur PubMed.

[Link] Récupération du résumé d’une publication

Récupérez le résumé de la publication dont le PMID est 29701945. Vous utiliserez la méthode [Link]().
Affichez ce résumé.

[Link] Distribution des années de publication des articles relatifs à la barstar

En utilisant la méthode [Link](), récupérez tous les PMID relatifs à la barstar. Pour cela, pensez à
augmenter le paramètre retmax. Vos PMID seront stockés dans la liste pmids sous forme de chaînes de caractères.
Vérifiez sur PubMed que vous avez récupéré le bon nombre d’articles.
En utilisant maintenant la méthode [Link]() dans une boucle, récupérez la date de publication de
chaque article. Stockez l’année sous forme d’un nombre entier dans la liste years. Cette étape peut prendre une dizaine
de minutes, soyez patient. Vous pouvez afficher dans votre boucle un message qui indique où vous en êtes dans la
récupération des articles.
Vérifiez que votre liste years contient bien autant d’éléments que la liste pmids.
Calculez maintenant le nombre de publications par année. Vous créerez pour cela un dictionnaire freq qui aura pour
clé les années (oui, une clé de dictionnaire peut aussi être un entier) et pour valeur le nombre de publications associées
à une année donnée.
Créez une liste x qui contient les clés du dictionnaire freq. Ordonnez les valeurs dans x avec la méthode .sort().
Créez maintenant une seconde liste y qui contient, dans l’ordre, le nombre de publications associées à chaque année. Bien
évidemment, les listes x et y doivent avoir la même taille. Au fait, en quelle année la barstar apparaît pour la première
fois dans une publication scientifique ?
Ensuite, avec le module matplotlib (que nous aborderons prochainement), vous allez pouvoir afficher la distribution
des publications en fonction des années :

7. [Link]
8. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 199


Chapitre 19. Module Biopython 19.5. Exercices

1 import [Link] as plt


2
3 fig, ax = [Link]()
4 [Link](x, y)

Vous pouvez également ajouter un peu de cosmétique et enregistrer le graphique sur votre disque dur :
1 import [Link] as plt
2
3 fig, ax = [Link]()
4 [Link](x, y)
5
6 # Étiquetage des axes.
7 ax.set_xlabel("Années")
8 ax.set_ylabel("Nombre de publications")
9
10 # Ajout du titre du graphique.
11 ax.set_title("Distribution des publications qui mentionnent la barstar")
12
13 # Enregistrement sur le disque.
14 [Link]("distribution_barstar_annee.png")

200 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


CHAPITRE 20

Module NumPy

Le module NumPy 1 est incontournable en bioinformatique. Il permet d’effectuer des calculs sur des vecteurs ou des
matrices, élément par élément, via un nouveau type d’objet appelé array.

20.1 Installation et convention


Contrairement aux modules vus précédemment, NumPy n’est pas fourni avec la distribution Python de base. Avec la
distribution Miniconda que nous vous conseillons d’utiliser (consultez pour cela la documentation en ligne 2 ), vous pouvez
rapidement l’installer avec la commande :
$ conda install -c conda-forge numpy

Dans ce chapitre, nous vous montrerons quelques exemples d’utilisation du module NumPy pour vous convaincre de
sa pertinence. Ces exemples seront exécutés dans un notebook Jupyter.
1 Les cellules de code apparaitront de cette manière
2 dans un notebook Jupyter, avec des numéros de lignes à gauche.

Les résultats seront affichés de cette manière,


éventuellement sur plusieurs lignes.

20.2 Chargement du module


On charge le module NumPy avec la commande :
1 import numpy

Par convention, on utilise np comme nom raccourci pour NumPy :


1 import numpy as np

20.3 Objets de type array


Les objets de type array correspondent à des tableaux à une ou plusieurs dimensions et permettent d’effectuer du
calcul vectoriel. La fonction array() convertit un conteneur (comme une liste ou un tuple) en un objet de type array.
1. [Link]
2. [Link]

201
Chapitre 20. Module NumPy 20.3. Objets de type array

Voici un exemple de conversion d’une liste à une dimension en objet array :


1 import numpy as np
2 a = [1, 2, 3]
3 [Link](a)

array([1, 2, 3])

1 b = [Link](a)
2 b

array([1, 2, 3])

1 type(b)

[Link]

Nous avons converti la liste [1, 2, 3] en array. La fonction [Link]() accepte aussi comme argument un tuple,
ou un objet de type range.
Par ailleurs, lorsqu’on demande à Python d’afficher le contenu d’un objet array, le mot array et les symboles ([ et
]) sont utilisés pour le distinguer d’une liste (délimitée par les caractères [ et ]) ou d’un tuple (délimité par les caractères
( et )).

Remarque
Un objet array ne contient que des données homogènes, c’est-à-dire d’un type identique. Il est possible de créer un
objet array à partir d’une liste contenant des entiers et des chaînes de caractères, mais, dans ce cas, toutes les valeurs
seront comprises par NumPy comme des chaînes de caractères :
1 a = [Link]([1, 2, "tigre"])
2 a

array(['1', '2', 'tigre'], dtype='<U21')

Dans cet exemple, toutes les valeurs du array sont entre guillemets, indiquant qu’il s’agit de chaînes de caractères.
De même, il est possible de créer un objet array à partir d’une liste constituée d’entiers et de floats, mais toutes les
valeurs seront alors comprises par NumPy comme des floats :
1 b = [Link]([1, 2, 3.5])
2 b

array([1. , 2. , 3.5])

Ici, la notation 1. indique qu’il s’agit du float 1.0000... et pas de l’entier 1.

Sur un modèle similaire à la fonction range(), la fonction arange() permet de construire un array à une dimension :
1 [Link](10)

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

Comme avec range(), on peut spécifier en argument une borne de début, une borne de fin et un pas :
1 [Link](10, 0, -1)

array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1])

202 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.3. Objets de type array Chapitre 20. Module NumPy

Un autre avantage de la fonction arange() est qu’elle génère des objets array qui contiennent des entiers ou des
floats (ce qui n’est pas possible avec range()) selon l’argument qu’on lui passe. D’abord un entier :
1 [Link](10)

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

Puis un float :
1 [Link](10.0)

array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])

La différence fondamentale entre un objet array à une dimension et une liste (ou un tuple) est que celui-ci est
considéré comme un vecteur. Par conséquent, on peut effectuer des opérations vectorielles élément par élément sur
ce type d’objet, ce qui est bien commode lorsqu’on analyse de grandes quantités de données. Regardez ces exemples :
1 v = [Link](4)
2 v

array([0, 1, 2, 3])

On ajoute 1 à chacun des éléments de l’array v :


1 v + 1

array([1, 2, 3, 4])

On multiplie par 2 chacun des éléments de l’array v :


1 v * 2

array([0, 2, 4, 6])

Avec les listes, ces opérations n’auraient été possibles qu’en utilisant des boucles. Nous vous encourageons donc à
utiliser dorénavant les objets array lorsque vous aurez besoin de faire des opérations élément par élément.
Il est aussi possible de multiplier deux arrays entre eux. Le résultat correspond alors à la multiplication élément par
élément des deux arrays initiaux :
1 v * v

array([0, 1, 4, 9])

20.3.1 Array et dimensions


Il est aussi possible de construire des objets arrays à deux dimensions, il suffit de passer en argument une liste de
listes à la fonction array() :
1 w = [Link]([[1, 2], [3, 4], [5, 6]])
2 w

array([[1, 2],
[3, 4],
[5, 6]])

On peut aussi créer des tableaux à trois dimensions en passant comme argument à la fonction array() une liste de
listes de listes :
1 x = [Link]([[[1, 2], [2, 3]], [[4, 5], [5, 6]]])
2 x

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 203


Chapitre 20. Module NumPy 20.3. Objets de type array

array([[[1, 2],
[2, 3]],

[[4, 5],
[5, 6]]])

La fonction array() peut créer des tableaux à n’importe quel nombre de dimensions. Toutefois, cela devient vite
compliqué lorsqu’on dépasse trois dimensions. Retenez qu’un objet array à une dimension peut être assimilé à un vecteur,
un array à deux dimensions à une matrice. On peut généraliser ces objets mathématiques avec un nombre arbitraires de
dimensions, on parle alors de tenseur, qui sont représentés avec NumPy en array à n dimensions. Nous nous focaliserons
dans la suite sur des arrays à une dimension (1D) ou deux dimensions (2D).
Avant de continuer, il est important de définir comment sont organisés ces arrays 2D qui représentent des matrices.
Il s’agit de tableaux de nombres qui sont organisés en lignes et en colonnes comme le montre la figure 20.1. Les indices
indiqués dans cette figure seront définis un peu plus loin dans la rubrique Indices.

Figure 20.1 – Définition des lignes et colonnes dans un array 2D.

Voici quelques attributs intéressants pour décrire un objet array :


1 v = [Link](4)
2 v

array([0, 1, 2, 3])

1 w = [Link]([[1, 2], [3, 4], [5, 6]])


2 w

array([[1, 2],
[3, 4],
[5, 6]])

L’attribut .ndim renvoie le nombre de dimensions de l’array. Par exemple, 1 pour un vecteur et 2 pour une matrice :
1 [Link]

1 [Link]

204 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.3. Objets de type array Chapitre 20. Module NumPy

L’attribut .shape renvoie les dimensions sous forme d’un tuple. Dans le cas d’une matrice (array à deux dimensions),
la première valeur du tuple correspond au nombre de lignes et la seconde au nombre de colonnes.
1 [Link]

(4,)

1 [Link]

(3, 2)

Enfin, l’attribut .size renvoie le nombre total d’éléments contenus dans l’array :
1 [Link]

1 [Link]

20.3.2 Redimensionnement d’array


La méthode .reshape() renvoie un nouvel array avec les dimensions spécifiées en argument :
1 a = [Link](0, 6)
2 a

array([0, 1, 2, 3, 4, 5])

1 [Link]

(6,)

1 b = [Link]((2, 3))
2 b

array([[0, 1, 2],
[3, 4, 5]])

1 [Link]

(2, 3)

1 a

array([0, 1, 2, 3, 4, 5])

Notez bien que l’array initial a n’a pas été modifié et que [Link]((2, 3)) n’est pas la même chose que
[Link]((3, 2)) :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 205


Chapitre 20. Module NumPy 20.3. Objets de type array

1 c = [Link]((3, 2))
2 c

array([[0, 1],
[2, 3],
[4, 5]])

1 [Link]

(3, 2)

La méthode .reshape() attend que les nouvelles dimensions soient compatibles avec la dimension initiale de
l’objet array, c’est-à-dire que le nombre d’éléments contenus dans les différents arrays soit le même. Dans nos exemples
précédents, 6 = 2 × 3 = 3 × 2.
Si les nouvelles dimensions ne sont pas compatibles avec les dimensions initiales, la méthode .reshape() génère une
erreur.
1 a = [Link](0, 6)
2 a

array([0, 1, 2, 3, 4, 5])

1 [Link]

(6,)

1 d = [Link]((3, 4))

---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[36], line 1
----> 1 d = [Link]((3, 4))

ValueError: cannot reshape array of size 6 into shape (3,4)

La méthode .resize(), par contre, ne déclenche pas d’erreur dans une telle situation et ajoute des 0 jusqu’à ce que
le nouvel array soit rempli, ou bien coupe la liste initiale :
1 a = [Link](0, 6)
2 [Link]

(6,)

1 [Link]((3, 3), refcheck=False)


2 [Link]

(3, 3)

1 a

array([[0, 1, 2],
[3, 4, 5],
[0, 0, 0]])

206 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.3. Objets de type array Chapitre 20. Module NumPy

1 b = [Link](0, 10)
2 [Link]

(10,)

1 [Link]((2, 3), refcheck=False)


2 [Link]

(2, 3)

1 b

array([[0, 1, 2],
[3, 4, 5]])

Attention
• Cette modification de la forme de l’array par la méthode .resize() est faite « sur place » (in place), c’est-à-dire
que la méthode ne renvoie rien, mais l’array initial est bel et bien modifié (comme des méthodes sur les listes telles
que la méthode .reverse(), voir le chapitre 13 Plus sur les listes).
• Si l’option refcheck=False n’est pas présente, Python peut parfois renvoyer une erreur s’il existe des références
vers l’array qu’on souhaite modifier.

Enfin, il existe la fonction [Link]() qui, dans le cas d’un nouvel array plus grand que l’array initial, va répéter
l’array initial afin de remplir les cases manquantes :
1 a = [Link](0, 6)
2 [Link]

(6,)

1 c = [Link](a, (3, 5))


2 [Link]

(3, 5)

1 c

array([[0, 1, 2, 3, 4],
[5, 0, 1, 2, 3],
[4, 5, 0, 1, 2]])

1 a

array([0, 1, 2, 3, 4, 5])

Notez que la fonction [Link]() renvoie un nouvel array mais ne modifie pas l’array initial, contrairement à la
méthode .resize(), décrite ci-dessus.

Remarque
Depuis le début de ce chapitre, nous avons toujours montré l’affichage d’un array tel quel dans un notebook Jupyter :

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 207


Chapitre 20. Module NumPy 20.3. Objets de type array

1 a = [Link](range(10))
2 a

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

1 a2 = [Link]((3, 3))
2 a2

array([[1., 1., 1.],


[1., 1., 1.],
[1., 1., 1.]])

Nous avons déjà indiqué que Python affiche systématiquement le mot array ainsi que les parenthèses, crochets et
virgules pour séparer les éléments. Toutefois, si vous utilisez la fonction print(), l’affichage sera différent. Le mot array,
les parenthèses et les virgules disparaissent :
1 print(a)

[0 1 2 3 4 5 6 7 8 9]

1 print(a2)

[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]

Ceci peut amener des confusions, en particulier entre un array 1D :


[0 1 2 3 4 5 6 7 8 9]

et une liste contenant les mêmes éléments :


[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Dans ce cas, seule la présence ou l’absence de virgules permet de savoir s’il s’agit d’un array ou d’une liste.

20.3.3 Méthodes de calcul sur les arrays et l’argument axis


Chaque array NumPy possède une multitude de méthodes. Nombre d’entre elles permettent de faire des calculs de
base comme .mean() pour la moyenne, .sum() pour la somme, .std() pour l’écart-type, .max() pour extraire le
maximum, .min() pour extraire le minimum, etc. La liste exhaustive est disponible en ligne 3 . Par défaut, chacune de
ces méthodes effectuera l’opération sur l’array entier, quelle que soit sa dimensionnalité. Par exemple :
1 import random
2 ma_liste = list(range(8))
3 [Link](ma_liste)
4 ma_liste

[2, 7, 6, 4, 0, 3, 1, 5]

1 a = [Link](ma_liste, (4, 2))


2 a

3. [Link]

208 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.3. Objets de type array Chapitre 20. Module NumPy

array([[2, 7],
[6, 4],
[0, 3],
[1, 5]])

1 [Link]()

La méthode .max() a bien renvoyé la valeur maximale 7. Un argument très utile existant dans toutes ces méthodes
est axis. Pour un array 2D, axis=0 signifie qu’on fera l’opération le long de l’axe 0, à savoir les lignes. C’est-à-dire que
l’opération se fait en variant les lignes. On récupère ainsi une valeur par colonne :
1 [Link](axis=0)

array([6, 7])

Dans l’array 1D récupéré, le premier élément vaut 6 (maximum de la 1ère colonne) et le second vaut 7 (maximum
de la seconde colonne).
Avec axis=1, on fait une opération similaire, mais en faisant varier les colonnes. On récupère ainsi une valeur par
ligne :
1 [Link](axis=1)

array([7, 6, 3, 5])

L’array 1D récupéré a quatre éléments correspondant au maximum de chaque ligne.


On comprend la puissance de l’argument axis. À nouveau, il est possible, en une ligne, de faire des calculs qui
pourraient être fastidieux avec les listes traditionnelles.

20.3.4 Indices
Pour récupérer un ou plusieurs élément(s) d’un objet array, vous pouvez utiliser les indices, de la même manière
qu’avec les listes :
1 a = [Link](10)
2 a

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

1 a[1]

L’utilisation des tranches est aussi possible :


1 a[5:]

array([5, 6, 7, 8, 9])

Ainsi que les pas :


1 a[::2]

array([0, 2, 4, 6, 8])

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 209


Chapitre 20. Module NumPy 20.3. Objets de type array

Dans le cas d’un objet array à deux dimensions, vous pouvez récupérer une ligne complète (d’indice i), une colonne
complète (d’indice j) ou bien un seul élément. La figure 20.1 montre comment sont organisés les indices des lignes et
des colonnes :
1 a = [Link]([[1, 2], [3, 4]])
2 a

array([[1, 2],
[3, 4]])

1 a[:,0]

array([1, 3])

1 a[0,:]

array([1, 2])

La syntaxe a[i,:] renvoie la ligne d’indice i, et a[:,j] renvoie la colonne d’indice j. Les tranches sont aussi
utilisables sur un array à deux dimensions.
1 a[1, 1]

La syntaxe a[i, j] renvoie l’élément à la ligne d’indice i et à la colonne d’indice j. Notez que NumPy suit la
convention mathématiques des matrices 4 , à savoir, qu’on définit toujours un élément par sa ligne puis par sa
colonne. En mathématiques, l’élément ai j d’une matrice A se trouve à la ime ligne et à la jme colonne :

Remarque
Pour un array 2D, si un seul indice est donné, par exemple a[i], on récupère la ligne d’indice i sous forme d’array
1D :
1 a = [Link]([[1, 2], [3, 4]])
2 a

array([[1, 2],
[3, 4]])

1 a[0]

array([1, 2])

1 a[1]

array([3, 4])

Pour cette raison, la syntaxe a[i][j] est également valide pour récupérer un élément :
1 a[1, 1]

4. [Link]

210 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.3. Objets de type array Chapitre 20. Module NumPy

1 a[1][1]

Nous vous recommandons la syntaxe a[i, j], qui est plus proche de la définition mathématique d’un élément de
matrice 5 .

20.3.5 Copie d’arrays


Comme pour les listes, nous attirons votre attention sur la copie d’arrays :
1 a = [Link](5)
2 a

array([0, 1, 2, 3, 4])

1 b = a
2 b[2] = -300
3 b

array([ 0, 1, -300, 3, 4])

1 a

array([ 0, 1, -300, 3, 4])

Attention
Par défaut la copie d’arrays se fait par référence, comme pour tous les conteneurs en Python (listes, tuples, diction-
naires, etc.).

Afin d’éviter le problème, vous pouvez soit utiliser la fonction [Link](), qui crée une nouvelle copie distincte de
l’array initial, soit la fonction [Link](), comme pour les listes (voir chapitre 12 Plus sur les listes) :
1 a = [Link]((2, 2), 0)
2 a

array([[0, 0],
[0, 0]])

1 b = [Link](a)
2 b[1, 1] = -300
3 import copy
4 c = [Link](a)
5 c[1, 1] = -500
6 a

array([[0, 0],
[0, 0]])

1 b

5. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 211


Chapitre 20. Module NumPy 20.4. Construction automatique de matrices

array([[ 0, 0],
[ 0, -300]])

1 c

array([[ 0, 0],
[ 0, -500]])

La fonction [Link]() est expliquée dans la rubrique suivante.

Remarque
L’instruction b = [Link](a) réalise bien une copie distincte de l’array a, quelle que soit sa dimensionnalité. Ceci
n’était pas le cas avec la fonction list() pour les copies de listes à partir de la dimension deux (liste de listes) :
1 liste_1 = [[0, 0], [1, 1]]
2 liste_2 = list(liste_1)
3 import copy
4 liste_3 = [Link](liste_1)
5 liste_1[1][1] = -365
6 liste_2

[[0, 0], [1, -365]]

1 liste_3

[[0, 0], [1, 1]]

20.4 Construction automatique de matrices


Il est parfois pénible de construire une matrice (array à deux dimensions) à l’aide d’une liste de listes. Le module
NumPy possède quelques fonctions pratiques pour initialiser des matrices. Par exemple, Les fonctions zeros() et ones
() construisent des objets array contenant des 0 ou des 1. Il suffit de leur passer en argument un tuple indiquant les
dimensions voulues :
1 [Link]((2, 3))

array([[0., 0., 0.],


[0., 0., 0.]])

1 [Link]((3, 3))

array([[1., 1., 1.],


[1., 1., 1.],
[1., 1., 1.]])

Par défaut, les fonctions zeros() et ones() génèrent des floats, mais vous pouvez demander des entiers en passant
le type (par exemple int, float, etc.) en second argument :
1 [Link]((2,3), int)

array([[0, 0, 0],
[0, 0, 0]])

212 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.5. Chargement d’un array depuis un fichier Chapitre 20. Module NumPy

Enfin, si vous voulez construire une matrice avec autre chose que des 0 ou des 1, vous avez à votre disposition la
fonction full() :
1 [Link]((2, 3), 7, int)

array([[7, 7, 7],
[7, 7, 7]])

1 [Link]((2, 3), 7, float)

array([[ 7., 7., 7.],


[ 7., 7., 7.]])

Nous construisons ainsi une matrice constituée de 2 lignes et 3 colonnes. Celle-ci ne contient que le chiffre 7 sous
formes d’entiers (int) dans le premier cas et de floats dans le second.

20.5 Chargement d’un array depuis un fichier


Le module NumPy contient aussi des fonctions pour lire des données à partir de fichiers et créer des arrays automa-
tiquement. C’est très pratique, car la plupart du temps les données que l’on analyse proviennent de fichiers. La fonction
la plus simple à prendre en main est [Link](). Celle-ci lit un fichier organisé en lignes et colonnes. Par exemple,
imaginons que nous ayons un fichier [Link] contenant :
1 7 310
15 -4 35
78 95 79

La fonction prend en argument le nom du fichier et renvoie un array 2D directement :


1 [Link]("[Link]")

array([[ 1., 7., 310.],


[ 15., -4., 35.],
[ 78., 95., 79.]])

Pratique, non ? Attention toutefois aux points suivants :


• Chaque ligne doit avoir le même nombre de colonnes, la fonction ne gère pas les données manquantes.
• Chaque donnée est convertie en float, donc si une chaîne de caractères est rencontrée la fonction renvoie une erreur.
• Par défaut, les données doivent être séparées par n’importe quelle combinaison d’espace(s) et/ou de tabulations.
Nous vous conseillons de consulter la documentation complète 6 de cette fonction. En effet, [Link]() contient
de nombreux arguments permettant de récupérer telles ou telles lignes ou colonnes, d’ignorer des lignes de commentaire,
de changer le séparateur par défaut (par exemple la virgule , pour les fichiers .csv)… qui peuvent se révéler utiles.
L’opération inverse qui consiste à sauver un array dans un fichier se fait avec la fonction [Link]() :
1 a = [Link](range(1, 10), (3, 3))
2 a

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

1 [Link]("[Link]", a)

Ceci générera le fichier [Link] contenant les lignes suivantes :


1.000000000000000000e+00 2.000000000000000000e+00 3.000000000000000000e+00
4.000000000000000000e+00 5.000000000000000000e+00 6.000000000000000000e+00
7.000000000000000000e+00 8.000000000000000000e+00 9.000000000000000000e+00

6. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 213


Chapitre 20. Module NumPy 20.6. Concaténation d’arrays

La fonction [Link]() écrit par défaut les données comme des floats en notation scientifique. Il existe de
nombreuses options possibles 7 permettant de changer le format, les séparateurs, etc.

Pour aller plus loin


Il existe d’autres fonctions plus avancées telles que [Link]() 8 , gérant les données manquantes, ou encore
[Link]() 9 et [Link]() 10 , permettant de lire des données au format binaire. De même, il existe des fonctions ou mé-
thodes permettant d’écrire au format binaire : [Link]() 11 ou .tofile() 12 . Le format binaire possède en général l’extension
.npy ou .npz lorsque les données sont compressées. L’avantage d’écrire au format binaire est que cela prend moins de
place pour de gros tableaux de données.

20.6 Concaténation d’arrays


Il peut être très utile de concaténer un ou plusieurs arrays. Il existe pour cela plusieurs fonctions dans NumPy, nous
développerons celle qui nous parait la plus intuitive et directe : [Link]().
Pour les arrays 1D, [Link]() prend en argument un tuple contenant les arrays à concaténer :
1 a1 = [Link]((0, 1))
2 a2 = [Link]((3, 4))
3 a1

array([0, 1])

1 a2

array([3, 4])

1 [Link]((a1, a2))

array([0, 1, 3, 4])

L’ordre de la concaténation est important :


1 [Link]((a2, a1))

array([3, 4, 0, 1])

1 [Link]((a1, a2, a1, a2))

array([0, 1, 3, 4, 0, 1, 3, 4])

Pour les arrays 2D, ça se complique un peu, car on peut concaténer des lignes ou des colonnes ! Ainsi, np.
concatenate() prend un argument optionnel, à savoir axis. Comme nous l’avions expliqué plus haut, celui-ci va
indiquer à NumPy si on veut concaténer le long de l’axe 0 (les lignes) ou le long de l’axe 1 (les colonnes). Voyons un
exemple :
1 a1 = [Link]([Link](range(6)), (3, 2))
2 a2 = a1 * 5
3 a1

7. [Link]
8. [Link]
9. [Link]
10. [Link]
11. [Link]
12. [Link]

214 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.7. Un peu d’algèbre linéaire Chapitre 20. Module NumPy

array([[0, 1],
[2, 3],
[4, 5]])

1 a2

array([[ 0, 5],
[10, 15],
[20, 25]])

On concatène d’abord par ligne (axis=0), c’est-à-dire qu’on ajoute les lignes du second array a2 à celles de l’array
a1 :
1 [Link]((a1, a2), axis=0)

array([[ 0, 1],
[ 2, 3],
[ 4, 5],
[ 0, 5],
[10, 15],
[20, 25]])

Ensuite, on concatène par colonne (axis=1). Attention, il vaut bien veiller à ce que la concaténation soit possible en
terme de dimensionalité. Par exemple, lors de la concaténation par colonne, il faut que les deux arrays a1 et a2 aient le
même nombre de lignes :
1 [Link]((a1, a2), axis=1)

array([[ 0, 1, 0, 5],
[ 2, 3, 10, 15],
[ 4, 5, 20, 25]])

Ces opérations de concaténation sont très importantes. On les utilise par exemple si on a des données dans plusieurs
fichiers différents et qu’on veut les agréger dans un array unique. On verra qu’on peut faire le même genre de chose avec
les fameux Dataframes du module pandas. Lisez bien également les recommandations dans la dernière rubrique 17.1.10
Quelques conseils sur quand utiliser la concaténation d’arrays avec NumPy.

20.7 Un peu d’algèbre linéaire


Après avoir manipulé les objets array comme des vecteurs et des matrices, voici quelques fonctions pour faire de
l’algèbre linéaire.
La fonction transpose() renvoie la transposée 13 d’un array. Par exemple, pour une matrice :
1 a = [Link]([Link](1, 10), (3, 3))
2 a

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

1 [Link](a)

array([[1, 4, 7],
[2, 5, 8],
[3, 6, 9]])

13. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 215


Chapitre 20. Module NumPy 20.7. Un peu d’algèbre linéaire

Tout objet array possède un attribut .T qui contient la transposée, il est ainsi possible d’utiliser cette notation objet
plus compacte :
1 a.T

array([[1, 4, 7],
[2, 5, 8],
[3, 6, 9]])

La fonction dot() permet de multiplier deux matrices 14 :


1 a = [Link]([Link](4), (2, 2))
2 a

array([[0, 1],
[2, 3]])

1 [Link](a, a)

array([[ 2, 3],
[ 6, 11]])

1 a * a

array([[0, 1],
[4, 9]])

Notez bien que dot(a, a) renvoie le produit matriciel entre deux matrices, alors que l’opération a * a renvoie le
produit élément par élément.

Remarque
Dans le module NumPy, il existe également des objets de type matrix pour lesquels les multiplications de matrices
sont différents, mais nous ne les aborderons pas ici.

Pour toutes les opérations suivantes, nous utiliserons des fonctions du sous-module linalg de NumPy.
La fonction diag() permet de générer une matrice diagonale :
1 a = [Link]((1, 2, 3))
2 a

array([[1, 0, 0],
[0, 2, 0],
[0, 0, 3]])

La fonction inv() renvoie l’inverse d’une matrice carrée 15 :


1 [Link](a)

array([[1. , 0. , 0. ],
[0. , 0.5 , 0. ],
[0. , 0. , 0.33333333]])

La fonction det() renvoie le déterminant 16 d’une matrice carrée :


14. [Link]
15. [Link]
16. [Link]

216 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.8. Parcours de matrice et affectation de lignes et colonnes Chapitre 20. Module NumPy

1 [Link](a)

6.0

Enfin, la fonction eig() renvoie les vecteurs et valeurs propres :


1 [Link](a)

EigResult(eigenvalues=array([1., 2., 3.]), eigenvectors=array([[1., 0., 0.],


[0., 1., 0.],
[0., 0., 1.]]))

La fonction eig() renvoie un objet EigResult, qui contient les valeurs propres (eigenvalues) et les vecteurs
propres (eigenvectors), qu’on peut ensuite récupérer par affectation multiple :
1 eigvals, eigvecs = [Link](a)
2 eigvals

array([1., 2., 3.])

eigvals est un array 1D contenant les trois valeurs propres.


1 eigvecs

array([[1., 0., 0.],


[0., 1., 0.],
[0., 0., 1.]])

eigvecs est un array 2D contenant les trois vecteurs propres (un par ligne).

20.8 Parcours de matrice et affectation de lignes et colonnes


Lorsqu’on a une matrice, on est souvent amené à la parcourir par ligne ou par colonne. NumPy permet d’itérer
directement sur les lignes d’une array :
1 a = [Link]([Link](1, 10), (3, 3))
2 a

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

1 for row in a:
2 print(row, type(row))

[1 2 3] <class '[Link]'>
[4 5 6] <class '[Link]'>
[7 8 9] <class '[Link]'>

À chaque itération, la variable row est un array 1D correspondant à chaque ligne de la matrice a. Cela est du au fait
que l’utilisation d’un indiçage unique a[i] pour un array 2D correspond à sa ligne d’indice i (voir la rubrique Indices
ci-dessus).
Pour itérer sur les colonnes, on peut utiliser l’astuce d’itérer sur la transposée de l’array a, c’est-à-dire a.T :
1 for col in a.T:
2 print(col, type(col))

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 217


Chapitre 20. Module NumPy 20.9. Masques booléens

[1 4 7] <class '[Link]'>
[2 5 8] <class '[Link]'>
[3 6 9] <class '[Link]'>

À chaque itération, la variable col est un array 1D correspondant à chaque colonne de a.


On se souvient de l’affectation multiple x, y = 1, 2 qui permettait d’affecter des valeurs à plusieurs variables à la
fois. Il est possible d’utiliser cette fonctionnalité aussi avec les arrays NumPy :
1 a

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

1 a1, a2, a3 = a
2 a1

array([1, 2, 3])

1 a2

array([4, 5, 6])

1 a3

array([7, 8, 9])

Par défaut, l’affectation multiple se fait sur les lignes de l’array 2D. Cette fonctionnalité s’explique à nouveau par le
fait que pour NumPy, a[i] correspond à la ligne d’indice i d’un array 2D.
Pour utiliser l’affectation multiple sur les colonnes, il suffit d’utiliser la transposée a.T :
1 c1, c2, c3 = a.T
2 c1

array([1, 4, 7])

1 c2

array([2, 5, 8])

1 c3

array([3, 6, 9])

20.9 Masques booléens


Une fonctionnalité puissante des arrays NumPy est l’utilisation des masques booléens. Avant de les définir, il est
important d’introduire le concept d’arrays de booléens. Jusqu’à maintenant nous avions définis uniquement des arrays
avec des types numériques int ou float. Il est tout à fait possible de définir des arrays de booléens. La fonction [Link]()
vue précédemment nous permet d’en construire facilement :
1 [Link]((2, 2), True)

218 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.9. Masques booléens Chapitre 20. Module NumPy

array([[ True, True],


[ True, True]])

1 [Link]((2, 2), False)

array([[False, False],
[False, False]])

Au premier abord, nous n’en voyons pas forcément l’utilité… Mais qu’en est-il lorsqu’on utilise les opérateurs de
comparaison avec un array ? Et bien cela renvoie un array de booléens !
1 a = [Link]([Link](1, 10), (3, 3))
2 a

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

1 a > 5

array([[False, False, False],


[False, False, True],
[ True, True, True]])

1 a == 2

array([[False, True, False],


[False, False, False],
[False, False, False]])

Tous les éléments de l’array satisfaisant la condition seront à True, les autres à False. Il est même possible de
combiner plusieurs conditions avec les opérateurs logiques & et | (respectivement ET et OU) :
1 a = [Link]([Link](1, 10), (3, 3))
2 a

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

1 (a > 3) & (a % 2 == 0)

array([[False, False, False],


[ True, False, True],
[False, True, False]])

1 (a > 3) | (a % 2 == 0)

array([[False, True, False],


[ True, True, True],
[ True, True, True]])

• Les opérateurs logiques & et | s’appliquent sur les arrays et sont différents des opérateurs logiques and et or, qui
eux s’appliquent sur les booléens (True ou False).
• Il est conseillé de mettre entre parenthèses chaque condition afin d’éviter les ambiguïtés.

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 219


Chapitre 20. Module NumPy 20.9. Masques booléens

Maintenant que les arrays de booléens ont été introduits, nous pouvons définir les masques booléens :

Définition
Les masques booléens sont des arrays de booléens qui sont utilisés en tant qu’« indice » d’un array initial. Cela permet
de récupérer ou de modifier une partie de l’array initial.

Concrètement, il suffira d’utiliser un array et un opérateur de comparaison entre les crochets qui étaient dédiés à
l’indiçage :
1 a = [Link]([Link](1, 10), (3, 3))
2 a

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

Pour isoler tous les éléments de l’array a qui sont supérieurs à 5 :


1 a[a > 5]

array([6, 7, 8, 9])

Pour isoler tous les éléments de l’array a qui sont égaux à 2 :


1 a[a == 2]

array([2])

Pour isoler tous les éléments de l’array a qui sont non nuls :
1 a[a != 0]

array([1, 2, 3, 4, 5, 6, 7, 8, 9])

À chaque fois, on ne récupère que les éléments de l’array a qui satisfont la sélection. Toutefois, il est important de
remarquer que l’array renvoyé perd la dimensionnalité de l’array a initial, il s’agit systématiquement d’un array 1D.
La grande puissance de ce mécanisme est que l’on peut utiliser les masques booléens pour modifier les éléments que
l’on sélectionne :
1 a = [Link]([Link](1, 10), (3, 3))
2 a

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

On sélectionne les éléments de l’array a supérieurs à 5 :


1 a[a > 5]

array([6, 7, 8, 9])

On affecte la valeur -1 aux éléments de l’array a supérieurs à 5 :


1 a[a > 5] = -1
2 a

220 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.10. Quelques conseils Chapitre 20. Module NumPy

array([[ 1, 2, 3],
[ 4, 5, -1],
[-1, -1, -1]])

On peut bien sûr combiner plusieurs conditions avec les opérateurs logiques :
1 a = [Link]([Link](1, 10), (3, 3))
2 a

array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

1 a[(a > 3) | (a % 2 == 0)] = 0


2 a

array([[1, 0, 3],
[0, 0, 0],
[0, 0, 0]])

Ce mécanisme de sélection avec des masques booléens se révèle très puissant pour manipuler de grandes quantités
de données. On verra qu’il peut être également utilisé avec les Dataframes du module pandas.

Remarque
Les masques booléens ne doivent pas être confondus avec les masked arrays 17 , qui sont des arrays dans lesquels on
peut trouver des valeurs manquantes ou invalides.

Enfin, une application possible des masques est de « binariser » une matrice de nombre :
1 import random
2 import numpy as np
3 a = [Link]([[Link]() for i in range(16)], (4, 4))
4 a

array([[0.58704728, 0.50212977, 0.70652863, 0.24158108],


[0.93102132, 0.41864373, 0.45807961, 0.98288744],
[0.48198211, 0.16877376, 0.14431518, 0.74784176],
[0.92913469, 0.08383269, 0.10670144, 0.14554345]])

1 seuil = 0.3
2 a[a < seuil] = 0
3 a[a > seuil] = 1
4 a

array([[1., 1., 1., 0.],


[1., 1., 1., 1.],
[1., 0., 0., 1.],
[1., 0., 0., 0.]])

On obtient ce résultat avec deux lignes de code en utilisant des arrays, alors qu’il aurait fallu faire des boucles avec
des listes classiques.

20.10 Quelques conseils


Nous vous avons présenté une petite partie du module NumPy, mais vous avez pu en constater son extraordinaire
puissance. On pourrait au premier abord être tenté d’abandonner les listes, toutefois elles gardent toute leur importance.
17. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 221


Chapitre 20. Module NumPy 20.11. Exercices

Alors, quand utiliser les listes ou quand utiliser les arrays NumPy ? Voici une liste non exhaustive d’éléments qui peuvent
guider votre choix :
Utilisez NumPy pour :
• les opérations vectorielles (éléments par éléments) ;
• lorsque vous souhaitez manipuler des objets mathématiques (vecteurs, matrices, etc.) et les outils associés (algèbre
linéaire) ;
• tout ce qui est numérique de manière générale.
Utilisez les listes :
• Lorsque vous avez besoin d’un conteneur pour accumuler des valeurs (fussent-elles des sous-listes), surtout lors-
qu’elles ne sont pas homogènes (c’est-à-dire du même type).
• Lorsque vous souhaitez accumuler des valeurs au fur et à mesure des itérations d’une boucle. Pour cela, la méthode
.append() des listes est bien plus efficace que de faire grandir un array ligne par ligne (c’est-à-dire en ajoutant
une ligne avec [Link]() à chaque itération).
• Lorsqu’on ne peut pas utiliser les fonctions de lecture de fichier de NumPy pour quelque raison que ce soit, il est
tout à fait classique de faire grandir une liste au fur et à mesure de la lecture du fichier puis de la convertir à la fin
en array. De manière générale, utilisez [Link]() seulement pour concaténer des gros arrays, pas pour
ajouter une seule ligne.
Enfin, comme nous vous le conseillons depuis le début, soignez votre documentation (docstrings) et vos commentaires
lorsque vous utilisez des arrays. NumPy permet de réaliser des opérations vectorielles de manière très compacte. Il est
donc essentiel de se mettre à la place du lecteur de votre script (y compris vous dans quelques semaines ou mois) et de
documenter ce que contient chaque array ainsi que sa dimensionnalité (1D, 2D, etc.).
Le module NumPy est la brique de base du calcul numérique en Python. Associé aux modules SciPy 18 et matplotlib,
ainsi qu’aux notebooks Jupyter (voir le chapitre précédent), il permet de faire du calcul scientifique de manière très
efficace. On verra dans le chapitre 22 Module Pandas que la puissance de NumPy est également utilisée par le module
pandas pour faire de l’analyse de données.

Pour aller plus loin


• Le livre de Nicolas Rougier From Python to Numpy 19 est une excellente ressource pour explorer plus en détails les
possibilités de NumPy.
• Les tutoriels 20 proposés par les développeurs de NumPy sont également un bon moyen de poursuivre votre explo-
ration de cette bibliothèque incontournable en sciences.

20.11 Exercices

Conseil
Pour ces exercices, utilisez des notebooks Jupyter.

20.11.1 Nombres pairs et impairs


Soit impairs un array NumPy qui contient les nombres :
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21

En une seule instruction, construisez l’array pairs dans lequel tous les éléments de impairs sont incrémentés de 1.
Comparez ce que vous venez de faire avec l’exercice « Nombres pairs et impairs » du chapitre 5 Boucles et comparaisons.

18. [Link]
19. [Link]
20. [Link]

222 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.11. Exercices Chapitre 20. Module NumPy

20.11.2 Distance entre deux atomes carbones alpha consécutifs de la barstar


La barstar est un inhibiteur de ribonucléase. C’est une protéine relativement simple qui contient 89 acides aminés. Sa
structure tridimensionnelle, obtenue par résonance magnétique nucléaire (RMN), se trouve dans la Protein Data Bank
(PDB) sous le code 1BTA.
L’objectif de cet exercice est de calculer la distance entre carbones alpha consécutifs le long de la chaîne peptidique
avec module NumPy et de découvrir une anomalie.
Le morceau de code suivant vous sera utile pour extraire les coordonnées atomiques des carbones alpha de la barstar
depuis un fichier PDB :
1 with open("[Link]", "r") as f_pdb, open("1bta_CA.txt", "w") as f_CA:
2 for ligne in f_pdb:
3 if [Link]("ATOM") and ligne[12:16].strip() == "CA":
4 x = ligne[30:38]
5 y = ligne[38:46]
6 z = ligne[46:54]
7 f_CA.write(f"{x} {y} {z} ")

• Ligne 1. On ouvre deux fichiers simultanément. Ici, le fichier [Link] est ouvert en lecture (r) et le fichier
1bta_CA.txt est ouvert en écriture (w).
• Pour chaque ligne du fichier PDB (ligne 2), si la ligne débute par ATOM et le nom de l’atome est CA (ligne 3),
alors on extrait les coordonnées atomiques (lignes 4 à 6) et on les écrit dans le fichier 1bta_CA.txt (ligne 7).
Les coordonnées sont toutes enregistrées sur une seule ligne, les unes après les autres.
Voici les étapes à suivre :
1. Extraction des coordonnées atomiques
• Téléchargez le fichier [Link] qui correspond à la structure de la barstar 21 sur le site de la PDB (lien direct
vers le fichier 22 ).
• Utilisez le code précédent pour extraire les coordonnées atomiques des carbones alpha de la barstar.
2. Lecture des coordonnées
• Ouvrez le fichier 1bta_CA.txt avec Python et créez une liste contenant toutes les coordonnées sous forme
de floats avec les fonctions split() et float().
• Affichez à l’écran le nombre total de coordonnées.
3. Construction de la matrice de coordonnées
• En ouvrant dans un éditeur de texte le fichier [Link], trouvez le nombre d’acides aminés qui constituent
la barstar.
• Avec la fonction array() du module NumPy, convertissez la liste de coordonnées en array. Avec la fonction
reshape() de NumPy, construisez ensuite une matrice à deux dimensions contenant les coordonnées des
carbones alpha de la barstar. Affichez les dimensions de cette matrice.
4. Calcul de la distance
• Créez maintenant une matrice qui contient les coordonnées des n − 1 premiers carbones alpha et une autre
qui contient les coordonnées des n − 1 derniers carbones alpha. Affichez les dimensions des matrices pour
vérification.
• En utilisant les opérateurs mathématiques habituels (-, +, **2) et les fonctions sqrt() et sum() du module
NumPy, calculez la distance entre les atomes n et n + 1.
• Pour chaque atome, affichez le numéro de l’atome et la distance entre carbones alpha consécutifs avec un
chiffre après la virgule. Repérez la valeur surprenante.

20.11.3 Jour le plus chaud


Le fichier [Link] 23 contient un relevé de quatre températures pour chaque jour de la semaine :
Lun 12 11 14 12
Mar 12 10 14 11
Mer 11 11 14 13
[...]

21. [Link]
22. [Link]
23. [Link]

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 223


Chapitre 20. Module NumPy 20.11. Exercices

À l’aide du module NumPy, on souhaite déterminer quel est le jour de la semaine le plus chaud. Pour cela nous vous
proposons les étapes suivantes :
1. Récupérez le nom des jours de la semaine depuis le fichier et stockez-les dans une liste days.
2. Récupérez les valeurs de températures depuis le fichier et stockez-les dans un array 2D. La fonction [Link]
() 24 et son argument usecols vous seront utiles.
3. Parcourez chaque ligne de la matrice, calculez la température moyenne de chaque jour puis stockez-la dans une
liste mean_temps.
4. À l’aide des deux listes days et mean_temps, déterminez et affichez le jour le plus chaud.

20.11.4 Calcul du centre de masse d’une membrane


L’image de gauche de la figure 20.2 montre le cliché d’une membrane de POPC (cyan) entourée d’eau (bleu)
(coordonnées trouvées ici 25 ). Les atomes de phosphore des groupes phosphates sont représentés en boule de van der
Waals brune. Dans cet exercice, on cherche à calculer le centre de masse de la membrane, ainsi que le centre de masse
(COM) de chaque monocouche de phosphores. Ces COM sont représentés sous forme de croix dans le graphique de droite
de la figure 20.2.

Figure 20.2 – Cliché d’une membrane de POPC.

Les coordonnées cartésiennes (x, y, z) de chaque atome de phosphore (en Å) sont stockées dans le fichier coors_P.dat 26 ,
à raison d’un atome par ligne.
Nous vous proposons les étapes suivantes pour résoudre cet exercice à l’aide du module NumPy :
1. Récupérez les coordonnées des atomes de phosphore depuis le fichier coors_P.dat et stockez-les dans un array
2D (matrice) coors_P. La dimensionnalité de cette matrice est n × 3, avec n le nombre de phosphores.
2. Calculez le z moyen de tous les phosphores (nombre réel) et stockez-le dans la variable mean_z. La méthode
.mean() vous sera utile.
3. Avec des masques de booléens, récupérez les coordonnées des phosphores de la monocouche du haut dans un array
2D upper. Faites de même avec la monocouche du bas dans un array 2D lower.
4. Calculez le centre de masse COM de la membrane, ainsi que de la monocouche du haut COM_upper et du bas
COM_lower. Pensez aux méthodes de calcul sur les arrays et l’argument axis.
5. Une fois tout cela effectué, créez un graphique 3D pour représenter les différents centres de masse. Utilisez la
fonction scatter() du module matplotlib pour l’affichage en 3D 27 . Voici un squelette de programme pour vous
24. [Link]
25. [Link]
26. [Link]
27. [Link]

224 Cours de Python / Université Paris Cité / UFR Sciences du Vivant


20.11. Exercices Chapitre 20. Module NumPy

aider :
1 # Initialisation du graphique.
2 from mpl_toolkits.mplot3d import Axes3D
3 import [Link] as plt
4 fig = [Link]()
5 ax = fig.add_subplot(111, projection="3d")
6 [...]
7 # X, Y et Z sont des arrays 1D de n éléments.
8 # Par exemple X représente tous les x des P de la monocouche upper.
9 [...]
10 # Affichage de la couche upper.
11 [Link](X, Y, Z, c="salmon", marker="o")
12 # Affichage du COM de la couche upper.
13 [Link](x, y, z, c="red", marker="x")
14 [...]
15 # Affichage des étiquettes des axes et du titre.
16 ax.set_xlabel("x (Å)")
17 ax.set_ylabel("y (Å)")
18 ax.set_zlabel("z (Å)")
19 ax.set_title("Graphe 3D des phosphores")
20 [Link]()

Cours de Python / Université Paris Cité / UFR Sciences du Vivant 225

Vous aimerez peut-être aussi