<figure><img src="image_1.png" /></figure>

HAL
open science

# Introduction à la programmation Python pour la biologie

Patrick F. J. Fuchs, Pierre Poulain

► To cite this version:

Patrick F. J. Fuchs, Pierre Poulain. Introduction à la programmation Python pour la biologie. Master. Programmation Python, Paris, France. 2024, pp.385. hal-03264103v5

HAL Id: hal-03264103

https://hal.science/hal-03264103v5

Submitted on 6 Sep 2024

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_2.png" /></figure>

Distributed under a Creative Commons Attribution - ShareAlike 4.0 International License
# Cours de Python

## Introduction à la programmation Python pour la biologie

https://python.sdv.u-paris.fr/

<figure><img src="image_3.png" /></figure>

Patrick Fuchs et Pierre Poulain
prénom [point] nom [arobase] u-paris [point] fr

version du 29 août 2024

Université Paris Cité, France
# Table des matières

## Avant-propos 8

Quelques mots sur l'origine de ce cours 8

Remerciements 8

Le livre 8

## 1 Introduction 10

1.1 Qu'est-ce que Python? 10

1.2 Conseils pour l'apprentissage de Python 11

1.3 Conseils pour installer et configurer Python 11

1.4 Notations utilisées 11

1.5 Introduction au shell 12

1.6 Premier contact avec Python 12

1.7 Premier programme 13

1.8 Commentaires 14

1.9 Notion de bloc d'instructions et d'indentation 14

1.10 Autres ressources 15

## 2 Variables 16

2.1 Définition et création 16

2.2 Les types de variables 17

2.3 Nommage 18

2.4 Écriture scientifique 18

2.5 Opérations 19

2.6 La fonction type() 21

2.7 Conversion de types 21

2.8 Note sur le vocabulaire et la syntaxe 21

2.9 Minimum et maximum 22

2.10 Exercices 22

## 3 Affichage 24

3.1 La fonction print() 24

3.2 Écriture formatée et f-strings 25

3.3 Écriture scientifique 29
<table><tbody><tr><td>3.4</td><td>Exercices</td><td>29</td></tr><tr><td>4</td><td>Listes</td><td>31</td></tr><tr><td>4.1</td><td>Définition</td><td>31</td></tr><tr><td>4.2</td><td>Utilisation</td><td>31</td></tr><tr><td>4.3</td><td>Opération sur les listes</td><td>32</td></tr><tr><td>4.4</td><td>Indiçage négatif</td><td>33</td></tr><tr><td>4.5</td><td>Tranches</td><td>33</td></tr><tr><td>4.6</td><td>Fonction len()</td><td>34</td></tr><tr><td>4.7</td><td>Les fonctions range() et list()</td><td>34</td></tr><tr><td>4.8</td><td>Listes de listes</td><td>35</td></tr><tr><td>4.9</td><td>Minimum, maximum et somme d'une liste</td><td>35</td></tr><tr><td>4.10</td><td>Problème avec les copies de listes</td><td>35</td></tr><tr><td>4.11</td><td>Note sur le vocabulaire et la syntaxe</td><td>36</td></tr><tr><td>4.12</td><td>Exercices</td><td>36</td></tr><tr><td>5</td><td>Boucles et comparaisons</td><td>38</td></tr><tr><td>5.1</td><td>Boucles for</td><td>38</td></tr><tr><td>5.2</td><td>Comparaisons</td><td>41</td></tr><tr><td>5.3</td><td>Boucles while</td><td>42</td></tr><tr><td>5.4</td><td>Exercices</td><td>43</td></tr><tr><td>6</td><td>Tests</td><td>48</td></tr><tr><td>6.1</td><td>Définition</td><td>48</td></tr><tr><td>6.2</td><td>Tests à plusieurs cas</td><td>48</td></tr><tr><td>6.3</td><td>Importance de l'indentation</td><td>49</td></tr><tr><td>6.4</td><td>Tests multiples</td><td>50</td></tr><tr><td>6.5</td><td>Instructions break et continue</td><td>51</td></tr><tr><td>6.6</td><td>Tests de valeur sur des floats</td><td>51</td></tr><tr><td>6.7</td><td>Exercices</td><td>52</td></tr><tr><td>7</td><td>Fichiers</td><td>57</td></tr><tr><td>7.1</td><td>Lecture dans un fichier</td><td>57</td></tr><tr><td>7.2</td><td>Écriture dans un fichier</td><td>60</td></tr><tr><td>7.3</td><td>Ouvrir deux fichiers avec l'instruction with</td><td>61</td></tr><tr><td>7.4</td><td>Note sur les retours à la ligne sous Unix et sous Windows</td><td>61</td></tr><tr><td>7.5</td><td>Importance des conversions de types avec les fichiers</td><td>62</td></tr><tr><td>7.6</td><td>Du respect des formats de données et de fichiers</td><td>62</td></tr><tr><td>7.7</td><td>Exercices</td><td>62</td></tr><tr><td>8</td><td>Dictionnaires et tuples</td><td>65</td></tr><tr><td>8.1</td><td>Dictionnaires</td><td>65</td></tr><tr><td>8.2</td><td>Tuples</td><td>69</td></tr><tr><td>8.3</td><td>Exercices</td><td>72</td></tr><tr><td>9</td><td>Modules</td><td>75</td></tr><tr><td>9.1</td><td>Définition</td><td>75</td></tr><tr><td>9.2</td><td>Importation de modules</td><td>75</td></tr><tr><td>9.3</td><td>Obtenir de l'aide sur les modules importés</td><td>77</td></tr><tr><td>9.4</td><td>Quelques modules courants</td><td>78</td></tr><tr><td>9.5</td><td>Module random : génération de nombres aléatoires</td><td>79</td></tr><tr><td>9.6</td><td>Module sys : passage d'arguments</td><td>80</td></tr><tr><td>9.7</td><td>Module pathlib : gestion des fichiers et des répertoires</td><td>82</td></tr><tr><td>9.8</td><td>Exercices</td><td>83</td></tr></tbody></table>
<table><tbody><tr><td><strong>10 Fonctions</strong></td><td><strong>87</strong></td></tr><tr><td>10.1 Principe et généralités</td><td>87</td></tr><tr><td>10.2 Définition</td><td>88</td></tr><tr><td>10.3 Passage d'arguments</td><td>89</td></tr><tr><td>10.4 Renvoi de résultats</td><td>89</td></tr><tr><td>10.5 Arguments positionnels et arguments par mot-clé</td><td>90</td></tr><tr><td>10.6 Variables locales et variables globales</td><td>92</td></tr><tr><td>10.7 Principe DRY</td><td>96</td></tr><tr><td>10.8 Exercices</td><td>96</td></tr><tr><td><strong>11 Plus sur les chaînes de caractères</strong></td><td><strong>101</strong></td></tr><tr><td>11.1 Préambule</td><td>101</td></tr><tr><td>11.2 Chaînes de caractères et listes</td><td>101</td></tr><tr><td>11.3 Caractères spéciaux</td><td>102</td></tr><tr><td>11.4 Préfixe de chaîne de caractères</td><td>102</td></tr><tr><td>11.5 Méthodes associées aux chaînes de caractères</td><td>104</td></tr><tr><td>11.6 Extraction de valeurs numériques d'une chaîne de caractères</td><td>106</td></tr><tr><td>11.7 Fonction map()</td><td>106</td></tr><tr><td>11.8 Test d'appartenance</td><td>107</td></tr><tr><td>11.9 Conversion d'une liste de chaînes de caractères en une chaîne de caractères</td><td>107</td></tr><tr><td>11.10 Method chaining</td><td>108</td></tr><tr><td>11.11 Exercices</td><td>109</td></tr><tr><td><strong>12 Plus sur les listes</strong></td><td><strong>115</strong></td></tr><tr><td>12.1 Méthodes associées aux listes</td><td>115</td></tr><tr><td>12.2 Construction d'une liste par itération</td><td>118</td></tr><tr><td>12.3 Test d'appartenance</td><td>118</td></tr><tr><td>12.4 Fonction zip()</td><td>118</td></tr><tr><td>12.5 Copie de listes</td><td>119</td></tr><tr><td>12.6 Initialisation d'une liste de listes</td><td>121</td></tr><tr><td>12.7 Liste de compréhension</td><td>122</td></tr><tr><td>12.8 Exercices</td><td>124</td></tr><tr><td><strong>13 Plus sur les fonctions</strong></td><td><strong>127</strong></td></tr><tr><td>13.1 Appel d'une fonction dans une fonction</td><td>127</td></tr><tr><td>13.2 Fonctions récursives</td><td>128</td></tr><tr><td>13.3 Portée des variables</td><td>130</td></tr><tr><td>13.4 Portée des listes</td><td>131</td></tr><tr><td>13.5 Règle LGI</td><td>132</td></tr><tr><td>13.6 Recommandations</td><td>133</td></tr><tr><td>13.7 Exercices</td><td>134</td></tr><tr><td><strong>14 Conteneurs</strong></td><td><strong>136</strong></td></tr><tr><td>14.1 Généralités</td><td>136</td></tr><tr><td>14.2 Plus sur les dictionnaires</td><td>139</td></tr><tr><td>14.3 Plus sur les tuples</td><td>142</td></tr><tr><td>14.4 Sets et frozensets</td><td>146</td></tr><tr><td>14.5 Récapitulation des propriétés des conteneurs</td><td>149</td></tr><tr><td>14.6 Dictionnaires et sets de compréhension</td><td>150</td></tr><tr><td>14.7 Module collections</td><td>151</td></tr><tr><td>14.8 Exercices</td><td>152</td></tr></tbody></table>
<table><tbody><tr><td><strong>15 Création de modules</strong></td><td><strong>156</strong></td></tr><tr><td>15.1 Pourquoi créer ses propres modules?</td><td>156</td></tr><tr><td>15.2 Création d'un module</td><td>156</td></tr><tr><td>15.3 Utilisation de son propre module</td><td>157</td></tr><tr><td>15.4 Les docstrings</td><td>157</td></tr><tr><td>15.5 Visibilité des fonctions dans un module</td><td>158</td></tr><tr><td>15.6 Module ou script?</td><td>159</td></tr><tr><td>15.7 Exercice</td><td>160</td></tr><tr><td><strong>16 Bonnes pratiques en programmation Python</strong></td><td><strong>161</strong></td></tr><tr><td>16.1 De la bonne syntaxe avec la PEP 8</td><td>162</td></tr><tr><td>16.2 Les docstrings et la PEP 257</td><td>166</td></tr><tr><td>16.3 Outils de contrôle qualité du code</td><td>167</td></tr><tr><td>16.4 Outil de formatage automatique du code</td><td>169</td></tr><tr><td>16.5 Organisation du code</td><td>170</td></tr><tr><td>16.6 Conseils sur la conception d'un script</td><td>171</td></tr><tr><td>16.7 Pour terminer : la PEP 20</td><td>172</td></tr><tr><td><strong>17 Expressions régulières et parsing</strong></td><td><strong>174</strong></td></tr><tr><td>17.1 Définition et syntaxe</td><td>174</td></tr><tr><td>17.2 Quelques ressources en ligne</td><td>176</td></tr><tr><td>17.3 Le module re</td><td>176</td></tr><tr><td>17.4 Exercices</td><td>179</td></tr><tr><td><strong>18 Jupyter et ses notebooks</strong></td><td><strong>182</strong></td></tr><tr><td>18.1 Installation</td><td>182</td></tr><tr><td>18.2 JupyterLab</td><td>182</td></tr><tr><td>18.3 Création d'un notebook</td><td>183</td></tr><tr><td>18.4 Le format Markdown</td><td>186</td></tr><tr><td>18.5 Des graphiques dans les notebooks</td><td>186</td></tr><tr><td>18.6 Les magic commands</td><td>187</td></tr><tr><td>18.7 Lancement d'une commande Unix</td><td>189</td></tr><tr><td><strong>19 Module Biopython</strong></td><td><strong>192</strong></td></tr><tr><td>19.1 Installation et convention</td><td>192</td></tr><tr><td>19.2 Chargement du module</td><td>192</td></tr><tr><td>19.3 Manipulation de séquences</td><td>193</td></tr><tr><td>19.4 Interrogation de la base de données PubMed</td><td>193</td></tr><tr><td>19.5 Exercices</td><td>196</td></tr><tr><td><strong>20 Module NumPy</strong></td><td><strong>199</strong></td></tr><tr><td>20.1 Installation et convention</td><td>199</td></tr><tr><td>20.2 Chargement du module</td><td>199</td></tr><tr><td>20.3 Objets de type array</td><td>199</td></tr><tr><td>20.4 Construction automatique de matrices</td><td>210</td></tr><tr><td>20.5 Chargement d'un array depuis un fichier</td><td>211</td></tr><tr><td>20.6 Concaténation d'arrays</td><td>212</td></tr><tr><td>20.7 Un peu d'algèbre linéaire</td><td>213</td></tr><tr><td>20.8 Parcours de matrice et affectation de lignes et colonnes</td><td>215</td></tr><tr><td>20.9 Masques booléens</td><td>216</td></tr><tr><td>20.10 Quelques conseils</td><td>219</td></tr><tr><td>20.11 Exercices</td><td>220</td></tr></tbody></table>
<table><tbody><tr><td><strong>21 Module Matplotlib</strong></td><td><strong>224</strong></td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;21.1 Installation et convention</td><td>224</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;21.2 Chargement du module</td><td>224</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;21.3 Représentation en nuage de points</td><td>224</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;21.4 Représentation sous forme de courbe</td><td>226</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;21.5 Représentation en diagramme en bâtons</td><td>229</td></tr><tr><td><strong>22 Module Pandas</strong></td><td><strong>231</strong></td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;22.1 Installation et convention</td><td>231</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;22.2 Chargement du module</td><td>231</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;22.3 Series</td><td>232</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;22.4 Dataframes</td><td>233</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;22.5 Un exemple plus concret avec les kinases</td><td>240</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;22.6 Exercices</td><td>250</td></tr><tr><td><strong>23 Avoir la classe avec les objets</strong></td><td><strong>251</strong></td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;23.1 Construction d'une classe</td><td>252</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;23.2 Exercices</td><td>260</td></tr><tr><td><strong>24 Avoir plus la classe avec les objets</strong></td><td><strong>261</strong></td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;24.1 Espace de noms</td><td>261</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;24.2 Polymorphisme</td><td>265</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;24.3 Héritage</td><td>267</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;24.4 Composition</td><td>274</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;24.5 Différence entre les attributs de classe et d'instance</td><td>276</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;24.6 Accès et modifications des attributs depuis l'extérieur</td><td>280</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;24.7 Bonnes pratiques pour construire et manipuler ses classes</td><td>287</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;24.8 Note finale de sémantique</td><td>293</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;24.9 Exercices</td><td>294</td></tr><tr><td><strong>25 Fenêtres graphiques et Tkinter</strong></td><td><strong>297</strong></td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;25.1 Utilité d'une GUI</td><td>297</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;25.2 Quelques concepts liés à la programmation graphique</td><td>298</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;25.3 Notion de fonction callback</td><td>299</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;25.4 Prise en main du module Tkinter</td><td>300</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;25.5 Construire une application Tkinter avec une classe</td><td>302</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;25.6 Le widget canvas</td><td>303</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;25.7 Pour aller plus loin</td><td>309</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;25.8 Exercices</td><td>314</td></tr><tr><td><strong>26 Remarques complémentaires</strong></td><td><strong>317</strong></td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;26.1 Différences Python 2 et Python 3</td><td>317</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;26.2 Anciennes méthodes de formatage des chaînes de caractères</td><td>319</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;26.3 Gestion des exceptions</td><td>321</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;26.4 Shebang et /usr/bin/env python3</td><td>324</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;26.5 Passage d'arguments avec *args et **kwargs</td><td>325</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;26.6 Décorateurs</td><td>327</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;26.7 Un peu de transformée de Fourier avec NumPy</td><td>330</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;26.8 Sauvegardez votre historique de commandes</td><td>330</td></tr><tr><td><strong>27 Mini-projets</strong></td><td><strong>332</strong></td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;27.1 Description des projets</td><td>332</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;27.2 Accompagnement pas à pas</td><td>334</td></tr><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;27.3 Scripts de correction</td><td>348</td></tr></tbody></table>
A Quelques formats de données en biologie 349

A.1 FASTA 349

A.2 GenBank 351

A.3 PDB 353

A.4 Format XML, CSV et TSV 359

B Installation de Python 366

B.1 Que recommande-t-on pour l'installation de Python? 366

B.2 Installation de Python avec Miniconda 367

B.3 Utilisation de conda pour installer des modules complémentaires 375

B.4 Choisir un bon éditeur de texte 379

B.5 Comment se mettre dans le bon répertoire dans le shell 382

B.6 Python web et mobile 383
# Avant-propos

## Quelques mots sur l'origine de ce cours

Ce cours a été conçu à l'origine pour les étudiants débutants en programmation Python des filières de biologie et de biochimie de l'université Paris Diderot - Paris 7, devenue Université Paris Cité<fnref n="1" />; et plus spécialement pour les étudiants du master Biologie Informatique.

Si vous relevez des erreurs à la lecture de ce document, merci de nous les signaler.

Le cours est disponible en version HTML 2 et PDF 3.

## Remerciements

Merci à tous les contributeurs, occasionnels ou réguliers, entre autre : Jennifer Becq, Benoist Laurent, Hubert Santuz, Virginie Martiny, Romain Laurent, Benjamin Boyer, Jonathan Barnoud, Amélie Bâcle, Thibault Tubiana, Romain Retureau, Catherine Lesourd, Philippe Label, Rémi Cuchillo, Cédric Gageat, Philibert Malbranche, Mikaël Naveau, Alexandra Moine-Franel, Dominique Tinel, et plus généralement les promotions des masters de biologie informatique et in silico drug design, ainsi que du diplôme universitaire en bioinformatique intégrative.

Nous remercions tout particulièrement Sander Nabuurs pour la première version de ce cours remontant à 2003, Denis Mestivier pour les idées de certains exercices et Philip Guo pour son site Python Tutor<fnref n="4" />.

Enfin, merci à vous tous, les curieux de Python, qui avez été nombreux à nous envoyer des retours sur ce cours, à nous suggérer des améliorations et à nous signaler des coquilles. Cela rend le cours vivant et dynamique, continuez comme ça !

De nombreuses personnes nous ont aussi demandé les corrections des exercices. Nous ne les mettons pas sur le site afin d'éviter la tentation de les regarder trop vite, mais vous pouvez nous écrire et nous vous les enverrons.

## Le livre

Ce cours est également publié aux éditions Dunod sous le titre « Programmation en Python pour les sciences de la vie<fnref n="5" /> ». Le livre en est à sa 2e édition, vous pouvez vous le procurer dans toutes les bonnes librairies.

Afin de promouvoir le partage des connaissances et le logiciel libre, nos droits d'auteurs provenant de la vente de cet ouvrage sont reversés à deux associations : Wikimédia France<fnref n="6" /> qui s'occupe notamment de l'encyclopédie libre Wikipédia

<footnote n="1">1. https://www.u-paris.fr/</footnote>

<footnote n="2">2. https://python.sdv.u-paris.fr/index.html</footnote>

<footnote n="3">3. https://python.sdv.u-paris.fr/cours-python.pdf</footnote>

<footnote n="4">4. http://pythontutor.com/</footnote>

<footnote n="5">5. https://www.dunod.com/sciences-techniques/programmation-en-python-pour-sciences-vie-0</footnote>

<footnote n="6">6. https://www.wikimedia.fr/</footnote>
<figure><img src="image_4.png" /><figcaption>FIGURE 1 – Couverture livre Dunod, 2e édition.</figcaption></figure>

et NumFOCUS<sup>7</sup> qui soutient le développement de logiciels libres scientifiques et notamment l'écosystème scientifique autour de Python.

<footnote n="7">7. https://numfocus.org/</footnote>
# CHAPITRE 1

## Introduction

### 1.1 Qu'est-ce que Python ?

Le langage de programmation Python a été créé en 1989 par Guido van Rossum, aux Pays-Bas. Le nom Python vient d'un hommage à la série télévisée *Monty Python's Flying Circus* dont G. van Rossum est fan. La première version publique de ce langage a été publiée en 1991.

La dernière version de Python est la version 3. Plus précisément, la version 3.11 a été publiée en octobre 2022. La version 2 de Python est obsolète et n'est plus maintenue, évitez de l'utiliser.

La *Python Software Foundation*<fnref n="1" /> est l'association qui organise le développement de Python et anime la communauté de développeurs et d'utilisateurs.

Ce langage de programmation présente de nombreuses caractéristiques intéressantes :

* Il est multiplateforme. C'est-à-dire qu'il fonctionne sur de nombreux systèmes d'exploitation : Windows, Mac OS X, Linux, Android, iOS, depuis les mini-ordinateurs Raspberry Pi jusqu'aux supercalculateurs.

* Il est gratuit. Vous pouvez l'installer sur autant d'ordinateurs que vous voulez (même sur votre téléphone !).

* C'est un langage de haut niveau. Il demande relativement peu de connaissance sur le fonctionnement d'un ordinateur pour être utilisé.

* C'est un langage interprété. Un script Python n'a pas besoin d'être compilé pour être exécuté, contrairement à des langages comme le C ou le C++.

* Il est orienté objet. C'est-à-dire qu'il est possible de concevoir en Python des entités qui miment celles du monde réel (une molécule d'ADN, une protéine, un atome, etc.) avec un certain nombre de règles de fonctionnement et d'interactions.

* Il est relativement simple à prendre en main <fnref n="2" />.

* C'est le langage de programmation le plus utilisé au monde (voir les classements TIOBE <fnref n="3" /> et IEEE Spectrum <fnref n="4" />).

* Enfin, il est très utilisé en bioinformatique, chimie-informatique et plus généralement en analyse de données.

Toutes ces caractéristiques font que Python est désormais enseigné dans de nombreuses formations, du lycée à l'enseignement supérieur.

<footnote n="1">1. https://www.python.org/psf/</footnote>

<footnote n="2">2. Nous sommes d'accord, cette notion est très relative.</footnote>

<footnote n="3">3. https://www.tiobe.com/tiobe-index/</footnote>

<footnote n="4">4. https://spectrum.ieee.org/the-top-programming-languages-2023</footnote>
## 1.2 Conseils pour l'apprentissage de Python

Comme tout apprentissage, apprendre la programmation Python prend du temps et nécessite de pratiquer. Contrairement à d'autres activités scientifiques expérimentales (biologie moléculaire, chimie organique, électronique, etc.), programmer en Python ne nécessite pas de matériel coûteux, juste un ordinateur et éventuellement une connexion internet. Par ailleurs, Python est un programme informatique qui par définition ne se fatigue pas, est patient et toujours disponible. N'hésitez donc pas à pratiquer, pratiquer et pratiquer encore.

## 1.3 Conseils pour installer et configurer Python

Pour pratiquer la programmation Python, il est préférable que Python soit installé sur votre ordinateur. La bonne nouvelle est que vous pouvez installer gratuitement Python sur votre machine, que ce soit sous Windows, Mac OS X ou Linux. Nous donnons dans cette rubrique un résumé des points importants concernant cette installation. Tous les détails et la marche à suivre pas-à-pas sont donnés à l'adresse https://python.sdv.u-paris.fr/livre-dunod.

### 1.3.1 Python 2 ou Python 3 ?

Ce cours est basé sur la version 3 de Python, qui est désormais le standard.

Si, néanmoins, vous deviez un jour travailler sur un ancien programme écrit en Python 2, sachez qu'il existe quelques différences importantes entre Python 2 et Python 3. Le chapitre 26 *Remarques complémentaires* (en ligne) vous apportera plus de précisions.

### 1.3.2 Miniconda

Nous vous conseillons d'installer Miniconda<fnref n="5" />, logiciel gratuit, disponible pour Windows, Mac OS X et Linux, et qui installera pour vous Python 3.

Avec le gestionnaire de paquets conda, fourni avec Miniconda, vous pourrez installer des modules supplémentaires qui sont très utiles en bioinformatique (*NumPy*, *scipy*, * matplotlib*, *pandas*, *Biopython*), mais également Jupyter Lab qui vous permettra d'éditer des *notebooks* Jupyter. Vous trouverez en ligne<fnref n="6" /> une documentation pas-à-pas pour installer Miniconda, Python 3 et les modules supplémentaires qui seront utilisés dans ce cours.

### 1.3.3 Éditeur de texte

L'apprentissage d'un langage informatique comme Python va nécessiter d'écrire des lignes de codes à l'aide d'un éditeur de texte. Si vous êtes débutants, on vous conseille d'utiliser *notepad++* sous Windows, *BBEdit* ou *CotEditor* sous Mac OS X et *gedit* sous Linux. La configuration de ces éditeurs de texte est détaillée dans la rubrique *Installation de Python* disponible en ligne. Bien sûr, si vous préférez d'autres éditeurs comme *Visual Studio Code*, *Sublime Text*, *emacs*, *vim*, *geany*... utilisez-les !

À toute fin utile, on rappelle que les logiciels *Microsoft Word*, *WordPad* et *LibreOffice Writer* ne sont pas des éditeurs de texte, ce sont des traitements de texte qui ne peuvent pas et ne doivent pas être utilisés pour écrire du code informatique.

## 1.4 Notations utilisées

Dans cet ouvrage, les commandes, les instructions Python, les résultats et les contenus de fichiers sont indiqués avec cette police pour les éléments ponctuels ou

1. sous cette forme,

2. sur plusieurs lignes,

3. pour les éléments les plus longs.

Pour ces derniers, le numéro à gauche indique le numéro de la ligne et sera utilisé pour faire référence à une instruction particulière. Ce numéro n'est bien sûr là qu'à titre indicatif.

Par ailleurs, dans le cas de programmes, de contenus de fichiers ou de résultats trop longs pour être inclus dans leur intégralité, la notation [*...*] indique une coupure arbitraire de plusieurs caractères ou lignes.

<footnote n="5">5. https://conda.io/miniconda.html</footnote>

<footnote n="6">6. https://python.sdv.u-paris.fr/livre-dunod</footnote>
1.5 Introduction au shell

Un shell est un interpréteur de commandes interactif permettant d'interagir avec l'ordinateur. On utilisera le shell pour lancer l'interpréteur Python.

Pour approfondir la notion de shell, vous pouvez consulter les pages Wikipedia :

* du shell Unix 7 fonctionnant sous Mac OS X et Linux;

• du shell PowerShell 8 fonctionnant sous Windows.

Un shell possède toujours une invite de commande, c'est-à-dire un message qui s'affiche avant l'endroit où on entre des commandes. Dans tout cet ouvrage, cette invite est représentée par convention par le symbole dollar $ (qui n'a rien à avoir ici avec la monnaie), et ce quel que soit le système d'exploitation.

Par exemple, si on vous demande de lancer l'instruction suivante :

$ python

il faudra taper seulement python sans le $ ni l'espace après le $.

1.6 Premier contact avec Python

Python est un langage interprété, c'est-à-dire que chaque ligne de code est lue puis interprétée afin d'être exécutée
par l'ordinateur. Pour vous en rendre compte, ouvrez un shell puis lancez la commande :

python

La commande précédente va lancer l'**interpréteur Python**. Vous devriez obtenir quelque chose de ce style pour Windows :

```
PS C:\Users\pierre>python
Python 3.12.2 | packaged by Anaconda, Inc. | (main, Feb 27 2024, 17:28:07) [MSC v.1916 64 bit (AMD64)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

pour Mac OS X :

```
iMac-de-pierre:Downloads$ python
Python 3.12.2 | packaged by Anaconda, Inc. | (main, Feb 27 2024, 12:57:28) [Clang 14.0.6 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

ou pour Linux :

```
pierre@jeera:~$ python
Python 3.12.2 | packaged by conda-forge | (main, Feb 16 2024, 20:50:58) [GCC 12.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

Les blocs

* PS C:\Users\pierre> pour Windows,

• iMac-de-pierre:Downloads$ pour Mac OS X,

• pierre@jeera:~$ pour Linux.

représentent l' invite de commande de votre shell . Il se peut que vous ayez aussi le mot ( base ) qui indique que vous avez un environnement conda activé. Par la suite, cette invite de commande sera représentée simplement par le caractère $ , que vous soyez sous Windows, Mac OS X ou Linux.

Le triple chevron >>> est l' invite de commande ( prompt en anglais) de l' interpréteur Python. Ici, Python attend une
commande que vous devez saisir au clavier. Tapez par exemple l'instruction :

print("Hello world!")

puis validez cette commande en appuyant sur la touche Entrée .

Python a exécuté la commande directement et a affiché le texte Hello world!. Il attend ensuite une nouvelle instruction en affichant l' invite de l' interpréteur Python ( >>> ). En résumé, voici ce qui a dû apparaître sur votre écran :

<footnote n="7">7. https://fr.wikipedia.org/wiki/Shell_Unix</footnote>

<footnote n="8">8. https://fr.wikipedia.org/wiki/Windows_PowerShell</footnote>
1 >>> print("Hello world!")

2 Hello world!

3 >>>

Vous pouvez refaire un nouvel essai en vous servant cette fois de l'interpréteur comme d'une calculatrice :

1 >>> 1+1

2 2

3 >>> 6*3

4 18

À ce stade, vous pouvez entrer une autre commande ou bien quitter l'interpréteur Python, soit en tapant la commande `exit()` puis en validant en appuyant sur la touche *Entrée*, soit en pressant simultanément les touches `Ctrl` et `D` sous Linux et Mac OS X ou `Ctrl` et `Z` puis *Entrée* sous Windows.

En résumant, l'interpréteur fonctionne sur le modèle :

1 >>> instruction python

2 résultat

où le triple chevron correspond à l'entrée (*input*) que l'utilisateur tape au clavier, et l'absence de chevron en début de ligne correspond à la sortie (*output*) générée par Python. Une exception se présente toutefois : lorsqu'on a une longue ligne de code, on peut la couper en deux avec le caractère `\` (*backslash*) pour des raisons de lisibilité :

1 >>> Voici une longue ligne de code \

2 ... décrite sur deux lignes

3 résultat

En ligne 1 on a rentré la première partie de la ligne de code. On termine par un `\`, ainsi Python sait que la ligne de code n'est pas finie. L'interpréteur nous l'indique avec les trois points `...`. En ligne 2, on rentre la fin de la ligne de code puis on appuie sur *Entrée*. À ce moment, Python nous génère le résultat. Si la ligne de code est vraiment très longue, il est même possible de la découper en trois voire plus :

1 >>> Voici une ligne de code qui \

2 ... est vraiment très longue car \

3 ... elle est découpée sur trois lignes

4 résultat

L'interpréteur Python est donc un système interactif dans lequel vous pouvez entrer des commandes, que Python exécutera sous vos yeux (au moment où vous validerez la commande en appuyant sur la touche *Entrée*).

Il existe de nombreux autres langages interprétés comme Perl<fnref n="9" /> ou R<fnref n="10" />. Le gros avantage de ce type de langage est qu'on peut immédiatement tester une commande à l'aide de l'interpréteur, ce qui est très utile pour débugger (c'est-à-dire trouver et corriger les éventuelles erreurs d'un programme). Gardez bien en mémoire cette propriété de Python qui pourra parfois vous faire gagner un temps précieux !

## 1.7 Premier programme

Bien sûr, l'interpréteur présente vite des limites dès lors que l'on veut exécuter une suite d'instructions plus complexe. Comme tout langage informatique, on peut enregistrer ces instructions dans un fichier, que l'on appelle communément un script (ou programme) Python.

Pour reprendre l'exemple précédent, ouvrez un éditeur de texte (pour choisir et configurer un éditeur de texte, reportez-vous si nécessaire à la rubrique *Installation de Python* en ligne<fnref n="11" />) et entrez le code suivant :

print("Hello world!")

Ensuite, enregistrez votre fichier sous le nom `test.py`, puis quittez l'éditeur de texte.

### Remarque

L'extension de fichier standard des scripts Python est `.py`.

<footnote n="9">9. http://www.perl.org</footnote>

<footnote n="10">10. http://www.r-project.org</footnote>

<footnote n="11">11. https://python.sdv.u-paris.fr/livre-dunod</footnote>
Pour exécuter votre script, ouvrez un shell et entrez la commande : `python test.py`

Vous devriez obtenir un résultat similaire à ceci :

```
$ python test.py
Hello world!
```

Si c'est bien le cas, bravo ! Vous avez exécuté votre premier programme Python.

## 1.8 Commentaires

Dans un script, tout ce qui suit le caractère # est ignoré par Python jusqu'à la fin de la ligne et est considéré comme un commentaire.

Les commentaires doivent expliquer votre code dans un langage humain. L'utilisation des commentaires est rediscutée dans le chapitre 16 *Bonnes pratiques en programmation Python*.

Voici un exemple :

```python
1 # Votre premier commentaire en Python.
2 print("Hello world!")
3
4 # D'autres commandes plus utiles pourraient suivre.
```

### Remarque

On appelle souvent à tort le caractère # « dièse ». On devrait plutôt parler de « croisillon <fnref n="12" /> ».

## 1.9 Notion de bloc d'instructions et d'indentation

En programmation, il est courant de répéter un certain nombre de choses (avec les boucles, voir le chapitre 5 *Boucles et comparaisons*) ou d'exécuter plusieurs instructions si une condition est vraie (avec les tests, voir le chapitre 6 *Tests*).

Par exemple, imaginons que nous souhaitions afficher chacune des bases d'une séquence d'ADN, les compter puis afficher le nombre total de bases à la fin. Nous pourrions utiliser l'algorithme présenté en pseudo-code dans la figure 1.1.

<figure><img src="image_5.png" /><figcaption>FIGURE 1.1 – Notion d'indentation et de bloc d'instructions.</figcaption></figure>

Pour chaque base de la séquence ATCCGACTG, nous souhaitons effectuer deux actions : d'abord afficher la base puis compter une base de plus. Pour indiquer cela, on décalera vers la droite ces deux instructions par rapport à la ligne précédente (pour chaque base [...]). Ce décalage est appelé **indentation**, et l'ensemble des lignes indentées constitue un **bloc d'instructions**.

Une fois qu'on aura réalisé ces deux actions sur chaque base, on pourra passer à la suite, c'est-à-dire afficher la taille de la séquence. Pour bien préciser que cet affichage se fait à la fin, donc une fois l'affichage puis le comptage de chaque base terminés, la ligne correspondante n'est pas indentée (c'est-à-dire qu'elle n'est pas décalée vers la droite).

<footnote n="12">12. https://fr.wikipedia.org/wiki/Croisillon_(signe)</footnote>
Pratiquement, l'indentation en Python doit être homogène (soit des espaces, soit des tabulations, mais pas un mélange des deux). Une indentation avec 4 espaces est le style d'indentation recommandé (voir le chapitre 16 *Bonnes pratiques en programmation Python*).

Si tout cela semble un peu complexe, ne vous inquiétez pas. Vous allez comprendre tous ces détails chapitre après chapitre.

## 1.10 Autres ressources

Pour compléter votre apprentissage de Python, n'hésitez pas à consulter d'autres ressources complémentaires à cet ouvrage. D'autres auteurs abordent l'apprentissage de Python d'une autre manière. Nous vous conseillons les ressources suivantes en langue française :

* Le livre *Apprendre à programmer avec Python 3* de Gérard Swinnen. Cet ouvrage est téléchargeable gratuitement sur le site de Gérard Swinnen<fnref n="13" />. Les éditions Eyrolles proposent également la version papier de cet ouvrage.

* Le livre *Apprendre à programmer en Python avec PyZo et Jupyter Notebook* de Bob Cordeau et Laurent Pointal, publié aux éditions Dunod. Une partie de cet ouvrage est téléchargeable gratuitement sur le site de Laurent Pointal<fnref n="14" />.

* Le livre *Apprenez à programmer en Python* de Vincent Legoff<fnref n="15" /> que vous trouverez sur le site Openclassrooms. Et pour terminer, une ressource incontournable en langue anglaise :

* Le site www.python.org<fnref n="16" />. Il contient énormément d'informations et de liens sur Python. La page d'index des modules<fnref n="17" /> est particulièrement utile (et traduite en français).

<footnote n="13">13. http://www.inforef.be/swi/python.htm</footnote>

<footnote n="14">14. https://perso.limsi.fr/pointal/python:courspython3</footnote>

<footnote n="15">15. https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python</footnote>

<footnote n="16">16. http://www.python.org</footnote>

<footnote n="17">17. https://docs.python.org/fr/3/py-modindex.html</footnote>
# CHAPITRE 2

## Variables

### 2.1 Définition et création

**Définition**

Une **variable** est une zone de la mémoire de l'ordinateur dans laquelle une **valeur** est stockée. Aux yeux du programmeur, cette variable est définie par un **nom**, alors que pour l'ordinateur, il s'agit en fait d'une adresse, c'est-à-dire d'une zone particulière de la mémoire.

En Python, la **déclaration** d'une variable et son **initialisation** (c'est-à-dire la première valeur que l'on va stocker dedans) se font en même temps. Pour vous en convaincre, testez les instructions suivantes après avoir lancé l'interpréteur :

1 >>> x = 2

2 >>> x

3 2

**Ligne 1.** Dans cet exemple, nous avons déclaré, puis initialisé la variable x avec la valeur 2. Notez bien qu'en réalité, il s'est passé plusieurs choses :

* Python a « deviné » que la variable était un entier. On dit que Python est un langage au **typage dynamique**.

* Python a alloué (réservé) l'espace en mémoire pour y accueillir un entier. Chaque type de variable prend plus ou moins d'espace en mémoire. Python a aussi fait en sorte qu'on puisse retrouver la variable sous le nom x.

* Enfin, Python a assigné la valeur 2 à la variable x.

Dans d'autres langages (en C par exemple), il faut coder ces différentes étapes une par une. Python étant un langage dit de *haut niveau*, la simple instruction `x = 2` a suffi à réaliser les trois étapes en une fois !

**Lignes 2 et 3.** L'interpréteur nous a permis de connaître le contenu de la variable juste en tapant son nom. Retenez ceci car c'est une **spécificité de l'interpréteur Python**, très pratique pour chasser (debugger) les erreurs dans un programme. En revanche, la ligne d'un script Python qui contient seulement le nom d'une variable (sans aucune autre indication) n'affichera pas la valeur de la variable à l'écran lors de l'exécution (pour autant, cette instruction reste valide et ne générera pas d'erreur).

Depuis la version 3.10, l'interpréteur Python a amélioré ses messages d'erreur. Il est ainsi capable de suggérer des noms de variables existants lorsqu'on fait une faute de frappe :
1 >>> voyelles = "aeiouy"

2 >>> voyelle

3 Traceback (most recent call last):

4 File "<stdin>", line 1, in <module>

5 NameError: name 'voyelle' is not defined. Did you mean: 'voyelles'?

Si le mot qu'on tape n'est pas très éloigné, cela fonctionne également lorsqu'on se trompe à différents endroits du mot !

1 pharmacie = "vente de médicaments"

2 >>> farmacia

3 Traceback (most recent call last):

4 File "<stdin>", line 1, in <module>

5 NameError: name 'farmacia' is not defined. Did you mean: 'pharmacie'?

Revenons sur le signe = ci-dessus.

### Définition

Le symbole = est appelé **opérateur d'affectation**. Il permet d'assigner une valeur à une variable en Python. Cet opérateur s'utilise toujours de la droite vers la gauche. Par exemple, dans l'instruction `x = 2` ci-dessus, Python attribue la valeur située à droite (ici, 2) à la variable située à gauche (ici, x). D'autres langages de programmation comme R utilisent les symboles `<-` pour rendre l'affectation d'une variable plus explicite, par exemple `x <- 2`.

Voici d'autres cas de figures que vous rencontrerez avec l'opérateur = :

1 >>> x = 2

2 >>> y = x

3 >>> y

4 2

5 >>> x = 5 - 2

6 >>> x

7 3

**Ligne 2.** Ici on a un nom de variable à gauche et à droite de l'opérateur =. Dans ce cas, on garde la règle d'aller toujours de la droite vers la gauche. C'est donc le contenu de la variable y qui est affecté à la variable x.

**Ligne 5.** Comme on le verra plus bas, si on a à droite de l'opérateur = une expression, ici la soustraction `4 - 2`, celle-ci est d'abord évaluée et c'est le résultat de cette opération qui sera affecté à la variable x. On pourra noter également que la valeur de x précédente (2) a été écrasée.

### Attention

L'opérateur d'affectation = écrase systématiquement la valeur de la variable située à sa gauche si celle-ci existe déjà.

## 2.2 Les types de variables

### Définition

Le **type** d'une variable correspond à la nature de celle-ci. Les trois principaux types dont nous aurons besoin dans un premier temps sont les entiers (*integer* ou *int*), les nombres décimaux que nous appellerons *floats* et les chaînes de caractères (*string* ou *str*).

Bien sûr, il existe de nombreux autres types (par exemple, les booléens, les nombres complexes, etc.). Si vous n'êtes pas effrayés, vous pouvez vous en rendre compte ici<fnref n="1" />.

<footnote n="1">1. https://docs.python.org/fr/3.12/library/stdtypes.html</footnote>
Dans l'exemple précédent, nous avons stocké un nombre entier (*int*) dans la variable *x*, mais il est tout à fait possible de stocker des *floats*, des chaînes de caractères (*string* ou *str*) ou de nombreux autres types de variables que nous verrons par la suite :

1 >>> y =3.14
2 >>> y
3 3.14
4 >>> a ="bonjour"
5 >>> a
6 'bonjour'
7 >>> b ='salut'
8 >>> b
9 'salut'
10 >>> c='"""girafe"""
11 >>> c
12 'girafe'
13 >>> d='"'lion'"
14 >>> d
15 'lion'

Remarque

Python reconnaît certains types de variables automatiquement (entier, *float*). Par contre, pour une chaîne de caractères, il faut l'entourer de guillemets (doubles, simples, voire trois guillemets successifs doubles ou simples) afin d'indiquer à Python le début et la fin de la chaîne de caractères.

Dans l'interpréteur, l'affichage direct du contenu d'une chaîne de caractères se fait avec des guillemets simples, quel que soit le type de guillemets utilisé pour définir la chaîne de caractères.

En Python, comme dans la plupart des langages de programmation, c'est le point qui est utilisé comme séparateur décimal. Ainsi, `3.14` est un nombre reconnu comme un *float* en Python alors que ce n'est pas le cas de `3,14`.

Il existe également des variables de type *booléen*. Un *booléen*<fnref n="2" /> est une variable qui ne prend que deux valeurs : Vrai ou Faux. En python, on utilise pour cela les deux mots réservés *True* et *False* :

1 >>> var =True
2 >>> var2 =False
3 >>> var
4 True
5 >>> var2
6 False

Nous verrons l'utilité des *booléens* dans les chapitres 5 *Boucles* et 6 *Tests*.

2.3 Nommage

Le nom des variables en Python peut être constitué de lettres minuscules (a à z), de lettres majuscules (A à Z), de nombres (0 à 9) ou du caractère souligné (_). Vous ne pouvez pas utiliser d'espace dans un nom de variable.

Par ailleurs, un nom de variable ne doit pas débuter par un chiffre et il n'est pas recommandé de le faire débuter par le caractère _ (sauf cas très particuliers).

De plus, il faut absolument éviter d'utiliser un mot « réservé » par Python comme nom de variable (par exemple : `print`, `range`, `for`, `from`, etc.).

Dans la mesure du possible, il est conseillé de mettre des noms de variables explicites. Sauf dans de rares cas que nous expliquerons plus tard dans le cours, évitez les noms de variables à une lettre.

Enfin, Python est sensible à la casse, ce qui signifie que les variables `TesT`, `test` et `TEST` sont différentes.

2.4 Écriture scientifique

On peut écrire des nombres très grands ou très petits avec des puissances de 10 en utilisant le symbole `e` :

<footnote n="2">2. https://fr.wikipedia.org/wiki/Bool%C3%A9en</footnote>
1 >>> 1e6

2 1000000.0

3 >>> 3.12e-3

4 0.00312

On appelle cela écriture ou notation scientifique. On pourra noter deux choses importantes :

* 1e6 ou 3.12e-3 n'implique pas l'utilisation du nombre exponentiel e, mais signifie $1 \times 10^6$ ou $3.12 \times 10^{-3}$ respectivement;

* même si on ne met que des entiers à gauche et à droite du symbole e (comme dans `1e6`), Python génère systématiquement un `float`.

Enfin, vous avez sans doute constaté qu'il est parfois pénible d'écrire des nombres composés de beaucoup de chiffres, par exemple le nombre d'Avogadro $6.02214076 \times 10^{23}$ ou le nombre d'humains sur Terre $8094752749$ au 5 mars 2024 à 19h34. Pour s'y retrouver, Python autorise l'utilisation du caractère « souligné » (ou underscore) pour séparer des groupes de chiffres. Par exemple :

1 >>> avogadro_number = 6.022_140_76e23

2 >>> print(avogadro_number)

3 6.02214076e+23

4 >>> humans_on_earth = 8_094_752_749

5 >>> print(humans_on_earth)

6 8094752749

Dans ces exemples, le caractère `_` (underscore ou « souligné ») est utilisé pour séparer des groupes de trois chiffres, mais on peut faire ce qu'on veut :

1 >>> print(80_94_7527_49)

2 8094752749

# 2.5 Opérations

## 2.5.1 Opérations sur les types numériques

Les quatre opérations arithmétiques de base se font de manière simple sur les types numériques (nombres entiers et floats) :

1 >>> x = 45

2 >>> x + 2

3 47

4 >>> x - 2

5 43

6 >>> x * 3

7 135

8 >>> y = 2.5

9 >>> x - y

10 42.5

11 >>> (x * 10) + y

12 452.5

Remarquez toutefois que si vous mélangez les types entiers et `floats`, le résultat est renvoyé comme un `float` (car ce type est plus général). Par ailleurs, l'utilisation de parenthèses permet de gérer les priorités.

L'opérateur `/` effectue une division. Contrairement aux opérateurs `+`, `-` et `*`, celui-ci renvoie systématiquement un `float` :

1 >>> 3 / 4

2 0.75

3 >>> 2.5 / 2

4 1.25

L'opérateur puissance utilise les symboles `**` :

<footnote n="3">3. https://thepopulationproject.org/</footnote>
1 >>> 2**3

2 8

3 >>> 2**4

4 16

Pour obtenir le quotient et le reste d'une division entière (voir ici<fnref n="4" /> pour un petit rappel sur la division entière), on utilise respectivement les symboles `//` et ` modulo %` :

1 >>> 5 // 4

2 1

3 >>> 5 % 4

4 1

5 >>> 8 // 4

6 2

7 >>> 8 % 4

8 0

Les symboles `+`, `-`, `*`, `/`, `**`, `//` et `%` sont appelés **opérateurs**, car ils réalisent des opérations sur les variables.

Enfin, il existe des opérateurs « combinés » qui effectue une opération et une affectation en une seule étape :

1 >>> i = 0

2 >>> i = i + 1

3 >>> i

4 1

5 >>> i += 1

6 >>> i

7 2

8 >>> i += 2

9 >>> i

10 4

L'opérateur `+=` effectue une addition puis affecte le résultat à la même variable. Cette opération s'appelle une « incrémentation ».

Les opérateurs `-=`, `*=` et `/=` se comportent de manière similaire pour la soustraction, la multiplication et la division.

## 2.5.2 Opérations sur les chaînes de caractères

Pour les chaînes de caractères, deux opérations sont possibles, l'addition et la multiplication :

1 >>> chaine = "Salut"

2 >>> chaine

3 'Salut'

4 >>> chaine + " Python"

5 'Salut Python'

6 >>> chaine * 3

7 'SalutSalutSalut'

L'opérateur d'addition `+` concatène (assemble) deux chaînes de caractères. On parle de **concaténation**.

L'opérateur de multiplication `*` entre un nombre entier et une chaîne de caractères duplique (répète) plusieurs fois une chaîne de caractères. On parle de **duplication**.

### Attention

Vous observez que les opérateurs `+` et `*` se comportent différemment s'il s'agit d'entiers ou de chaînes de caractères. Ainsi, l'opération `2 + 2` est une addition alors que l'opération `"2" + "2"` est une **concaténation**. On appelle ce comportement **redéfinition des opérateurs**. Nous serons amenés à revoir cette notion dans le chapitre 24 *Avoir plus la classe avec les objets* (en ligne).

## 2.5.3 Opérations illicites

Attention à ne pas faire d'opération illicite car vous obtiendriez un message d'erreur :

<footnote n="4">4. https://fr.wikipedia.org/wiki/Division_euclidienne</footnote>
```python
1 >>> "toto" * 1.3
2 Traceback (most recent call last):
3     File "<stdin>", line 1, in <module>
4 TypeError: can't multiply sequence by non-int of type 'float'
5 >>> "toto" + 2
6 Traceback (most recent call last):
7     File "<stdin>", line 1, in <module>
8 TypeError: can only concatenate str (not "int") to str
```

Notez que Python vous donne des informations dans son message d'erreur. Dans le second exemple, il indique que vous devez utiliser une variable de type `str` c'est-à-dire une chaîne de caractères et pas un `int`, c'est-à-dire un entier.

## 2.6 La fonction type()

Si vous ne vous souvenez plus du type d'une variable, utilisez la fonction `type()` qui vous le rappellera.

```python
1 >>> x = 2
2 >>> type(x)
3 <class 'int'>
4 >>> y = 2.0
5 >>> type(y)
6 <class 'float'>
7 >>> z = '2'
8 >>> type(z)
9 <class 'str'>
10 >>> type(True)
11 <class 'bool'>
```

Nous verrons plus tard ce que signifie le mot class.

### Attention

Pour Python, la valeur `2` (nombre entier) est différente de `2.0` (`float`) et est aussi différente de `'2'` (chaîne de caractères).

## 2.7 Conversion de types

En programmation, on est souvent amené à convertir les types, c'est-à-dire passer d'un type numérique à une chaîne de caractères ou vice-versa. En Python, rien de plus simple avec les fonctions `int()`, `float()` et `str()`. Pour vous en convaincre, regardez ces exemples :

```python
1 >>> i = 3
2 >>> str(i)
3 '3'
4 >>> i = '456'
5 >>> int(i)
6 456
7 >>> float(i)
8 456.0
9 >>> i = '3.1416'
10 >>> float(i)
11 3.1416
```

On verra au chapitre 7 *Fichiers* que ces conversions sont essentielles. En effet, lorsqu'on lit ou écrit des nombres dans un fichier, ils sont considérés comme du texte, donc des chaînes de caractères.

Toute conversion d'une variable d'un type en un autre est appelé *casting* en anglais, il se peut que vous croisiez ce terme si vous consultez d'autres ressources.

## 2.8 Note sur le vocabulaire et la syntaxe

Nous avons vu dans ce chapitre la notion de **variable** qui est commune à tous les langages de programmation. Toutefois, Python est un langage dit « orienté objet », il se peut que dans la suite du cours nous employions le mot
**objet** pour désigner une variable. Par exemple, « une variable de type entier » sera pour nous équivalent à « un objet de type entier ». Nous verrons dans le chapitre 23 *Avoir la classe avec les objets* (en ligne) ce que le mot « objet » signifie réellement (tout comme le mot « classe »).

Par ailleurs, nous avons rencontré plusieurs fois des **fonctions** dans ce chapitre, notamment avec `type()`, `int()`, `float()` et `str()`. Dans le chapitre 1 *Introduction*, nous avons également vu la fonction `print()`. On reconnaît qu'il s'agit d'une fonction car son nom est suivi de parenthèses (par exemple, `type()`). En Python, la syntaxe générale est `fonction()`.

Ce qui se trouve entre les parenthèses d'une fonction est appelé **argument** et c'est ce que l'on « passe » à la fonction. Dans l'instruction `type(2)`, c'est l'entier 2 qui est l'argument passé à la fonction `type()`. Pour l'instant, on retiendra qu'une fonction est une sorte de boîte à qui on passe un (ou plusieurs) argument(s), qui effectue une action et qui peut renvoyer un résultat ou plus généralement un objet. Par exemple, la fonction `type()` renvoie le type de la variable qu'on lui a passé en argument.

Si ces notions vous semblent obscures, ne vous inquiétez pas, au fur et à mesure que vous avancerez dans le cours, tout deviendra limpide.

## 2.9 Minimum et maximum

Python propose les fonctions `min()` et `max()` qui renvoient respectivement le minimum et le maximum de plusieurs entiers ou `floats` :

1. `>>> min(1, -2, 4)`

2. `-2`

3. `>>> pi = 3.14`

4. `>>> e = 2.71`

5. `>>> max(e, pi)`

6. `3.14`

7. `>>> max(1, 2.4, -6)`

8. `2.4`

Par rapport à la discussion de la rubrique précédente, `min()` et `max()` sont des exemples de fonctions prenant plusieurs arguments. En Python, quand une fonction prend plusieurs arguments, on doit les séparer par une virgule. `min()` et `max()` prennent en argument autant d'entiers et de `floats` que l'on veut, mais il en faut au moins deux.

## 2.10 Exercices

**Conseil**

Pour ces exercices, utilisez l'interpréteur Python.

### 2.10.1 Nombres de Friedman

Les nombres de Friedman<fnref n="5" /> sont des nombres qui peuvent s'exprimer avec tous leurs chiffres dans une expression mathématique.

Par exemple, 347 est un nombre de Friedman car il peut s'écrire sous la forme $4+7^3$. De même pour 127 qui peut s'écrire sous la forme $2^7-1$.

Déterminez si les expressions suivantes correspondent à des nombres de Friedman. Pour cela, vous les écrirez en Python puis exécuterez le code correspondant.

* $7+3^6$

* $(3+4)^3$

* $3^6-5$

* $(1+2^8) \times 5$

* $(2+1^8)^7$

<footnote n="5">5. https://fr.wikipedia.org/wiki/Nombre_de_Friedman</footnote>
2.10.2 Prédire le résultat : opérations

Essayez de prédire le résultat de chacune des instructions suivantes, puis vérifiez-le dans l'interpréteur Python :

• (1+2)**3

• "Da" * 4

• "Da" + 3

• ("Pa"+"La") * 2

• ("Da"*4) / 2

• 5 / 2

• 5 // 2

• 5 % 2

2.10.3 Prédire le résultat : opérations et conversions de types

Essayez de prédire le résultat de chacune des instructions suivantes, puis vérifiez-le dans l'interpréteur Python :

• str(4) * int("3")

• int("3") + float("3.2")

• str(3) * float("3.2")

• str(3/4) * 2
# CHAPITRE 3

## Affichage

### 3.1 La fonction print()

Dans le chapitre 1 *Introduction*, nous avons rencontré la fonction `print()` qui affiche une chaîne de caractères (le fameux `"Hello world!"`). En fait, la fonction `print()` affiche l'argument qu'on lui passe entre parenthèses **et** un retour à ligne. Ce retour à ligne supplémentaire est ajouté par défaut. Si toutefois, on ne veut pas afficher ce retour à la ligne, on peut utiliser l'argument par « mot-clé » `end` :

1 >>> print("Hello world!")

2 Hello world!

3 >>> print("Hello world!", end="")

4 Hello world! >>>

**Ligne 1.** On a utilisé l'instruction `print()` classiquement en passant la chaîne de caractères `"Hello world!"` en argument.

**Ligne 3.** On a ajouté un second argument `end=""`, en précisant le mot-clé `end`. Nous aborderons les arguments par mot-clé dans le chapitre 10 *Fonctions*. Pour l'instant, dites-vous que cela modifie le comportement par défaut des fonctions.

**Ligne 4.** L'effet de l'argument `end=""` est que les trois chevrons `>>>` se retrouvent collés après la chaîne de caractères `"Hello world!"`.

Une autre manière de s'en rendre compte est d'utiliser deux fonctions `print()` à la suite. Dans la portion de code suivante, le caractère « ; » sert à séparer plusieurs instructions Python sur une même ligne :

1 >>> print("Hello") ; print("Joe")

2 Hello

3 Joe

4 >>> print("Hello", end="") ; print("Joe")

5 HelloJoe

6 >>> print("Hello", end=" ") ; print("Joe")

7 Hello Joe

La fonction `print()` peut également afficher le contenu d'une variable quel que soit son type. Par exemple, pour un entier :

1 >>> var =3

2 >>> print(var)

3 3
Il est également possible d'afficher le contenu de plusieurs variables (quel que soit leur type) en les séparant par des virgules :

```python
1 >>> x = 32
2 >>> nom = "John"
3 >>> print(nom, "a", x, "ans")
4 John a 32 ans
```

Python a écrit une phrase complète en remplaçant les variables *x* et *nom* par leur contenu. Vous remarquerez que pour afficher plusieurs éléments de texte sur une seule ligne, nous avons utilisé le séparateur « , » entre les différents éléments. Python a également ajouté un espace à chaque fois que l'on utilisait le séparateur « , ». On peut modifier ce comportement en passant à la fonction *print()* l'argument par mot-clé *sep* :

```python
1 >>> x = 32
2 >>> nom = "John"
3 >>> print(nom, "a", x, "ans", sep="")
4 Johna32ans
5 >>> print(nom, "a", x, "ans", sep="-")
6 John-a-32-ans
7 >>> print(nom, "a", x, "ans", sep="_")
8 John_a_32_ans
```

Pour afficher deux chaînes de caractères l'une à côté de l'autre, sans espace, on peut soit les concaténer, soit utiliser l'argument par mot-clé *sep* avec une chaîne de caractères vide :

```python
1 >>> ani1 = "chat"
2 >>> ani2 = "souris"
3 >>> print(ani1, ani2)
4 chat souris
5 >>> print(ani1 + ani2)
6 chatsouris
7 >>> print(ani1, ani2, sep="")
8 chatsouris
```

## 3.2 Écriture formatée et f-strings

### 3.2.1 Définitions

**Définition**

L'écriture formatée est un mécanisme permettant d'afficher des variables avec un format précis, par exemple justifiées à gauche ou à droite, ou encore avec un certain nombre de décimales pour les *floats*. L'écriture formatée est incontournable lorsqu'on veut créer des fichiers organisés en « belles colonnes » comme par exemple les fichiers PDB (pour en savoir plus sur ce format, reportez-vous à l'annexe A *Quelques formats de données en biologie*).

Depuis la version 3.6, Python a introduit les *f-strings* pour mettre en place l'écriture formatée que nous allons décrire en détail dans cette rubrique. Il existe d'autres manières pour formater des chaînes de caractères qui étaient utilisées avant la version 3.6, nous expliquons cela dans le chapitre 26 *Remarques complémentaires* (en ligne). Toutefois, nous vous conseillons vivement l'utilisation des *f-strings* si vous débutez l'apprentissage de Python. Il est inutile d'apprendre les anciennes manières.

**Définition**

*f-string* est le diminutif de *formatted string literals*. Mais encore ? Dans le chapitre précédent, nous avons vu les chaînes de caractères ou encore *strings* qui étaient représentées par un texte entouré de guillemets simples ou doubles. Par exemple :

```python
1 "Ceci est une chaîne de caractères"
```

L'équivalent en *f-string* est la même chaîne de caractères précédée du caractère *f* **sans espace** entre les deux :

```python
1 f"Ceci est une chaîne de caractères"
```
Ce caractère f avant les guillemets va indiquer à Python qu'il s'agit d'une *f-string* mettant en place le mécanisme de l'écriture formatée, contrairement à une *string* normale.

Nous expliquons plus en détail dans le chapitre 11 *Plus sur les chaînes de caractères* pourquoi on doit mettre ce f et quel est le mécanisme sous-jacent.

### 3.2.2 Prise en main des f-strings

Les *f-strings* permettent une meilleure organisation de l'affichage des variables. Reprenons l'exemple ci-dessus à propos de notre ami John :

```python
1 >>> x = 32
2 >>> nom = "John"
3 >>> print(f"{nom} a {x} ans")
4 John a 32 ans
```

Il suffit de passer un nom de variable au sein de chaque couple d'accolades et Python les remplace par leur contenu. La syntaxe apparaît plus lisible que l'équivalent vu précédemment :

```python
1 >>> print(nom, "a", x, "ans")
2 John a 32 ans
```

Bien sûr, il ne faut pas omettre le f avant le premier guillemet, sinon Python prendra cela pour une chaîne de caractères normale et ne mettra pas en place le mécanisme de remplacement entre les accolades :

```python
1 >>> print("{nom} a {x} ans")
2 {nom} a {x} ans
```

#### Remarque

Une variable est utilisable plus d'une fois pour une *f-string* donnée :

```python
1 >>> var = "to"
2 >>> print(f"{var} et {var} font {var}{var}")
3 to et to font toto
4 >>>
```

Enfin, il est possible de mettre entre les accolades des valeurs numériques ou des chaînes de caractères :

```python
1 >>> print(f"J'affiche l'entier {10} et le float {3.14}")
2 J'affiche l'entier 10 et le float 3.14
3 >>> print(f"J'affiche la chaîne {'Python'}")
4 J'affiche la chaîne Python
```

Même si cela ne présente que peu d'intérêt pour l'instant, il s'agit d'une commande Python parfaitement valide. Nous verrons des exemples plus pertinents par la suite. Cela fonctionne avec n'importe quel type de variable (entiers, chaînes de caractères, *floats*, etc.). Attention toutefois pour les chaînes de caractères, utilisez des guillemets simples au sein des accolades si vous définissez votre *f-string* avec des guillemets doubles.

### 3.2.3 Spécification de format

Les *f-strings* permettent de remplacer des variables au sein d'une chaîne de caractères. On peut également spécifier le format de leur affichage.

Prenons un exemple. Imaginez que vous vouliez calculer, puis afficher, la proportion de GC d'un génome. 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) du génome considéré. Si on a, par exemple, 4 500 bases G et 2 575 bases C, pour un total de 14 800 bases, vous pourriez procéder comme suit (notez bien l'utilisation des parenthèses pour gérer les priorités des opérateurs) :

```python
1 >>> prop_GC = (4500 + 2575) / 14800
2 >>> print("La proportion de GC est", prop_GC)
3 La proportion de GC est 0.4780405405405405
```
Le résultat obtenu présente trop de décimales (seize dans le cas présent). Pour écrire le résultat plus lisiblement, vous pouvez spécifier dans les accolades {} le format qui vous intéresse. Dans le cas présent, vous voulez formater un float pour l'afficher avec deux puis trois décimales :

1 >>> print(f"La proportion de GC est {prop_GC:.2f}")

2 La proportion de GC est 0.48

3 >>> print(f"La proportion de GC est {prop_GC:.3f}")

4 La proportion de GC est 0.478

Détaillons le contenu des accolades de la première ligne (`{prop_GC:.2f}`) :

* D'abord on a le nom de la variable à formatter, `prop_GC`, c'est indispensable avec les *f-strings*.

* Ensuite on rencontre les deux-points :, ceux-ci indiquent que ce qui suit va spécifier le format dans lequel on veut afficher la variable `prop_GC`.

* À droite des deux-points on trouve `.2f` qui indique ce format : la lettre `f` indique qu'on souhaite afficher la variable sous forme d'un `float`, les caractères `.2` indiquent la précision voulue, soit ici deux chiffres après la virgule.

Notez enfin que le formatage avec `.xf` (x étant un entier positif) renvoie un résultat arrondi.

Vous pouvez aussi formater des entiers avec la lettre `d` (ici d veut dire *decimal integer*) :

1 >>> nb_G = 4500

2 >>> print(f"Ce génome contient {nb_G:d} guanines")

3 Ce génome contient 4500 guanines

ou mettre plusieurs nombres dans une même chaîne de caractères :

1 >>> nb_G = 4500

2 >>> nb_C = 2575

3 >>> print(f"Ce génome contient {nb_G:d} G et {nb_C:d} C, "

4 ... f"soit une proportion de {prop_GC:.2f}")

5 Ce génome contient 4500 G et 2575 C, soit une proportion de 0.48

6 >>> perc_GC = prop_GC * 100

7 >>> print(f"Ce génome contient {nb_G:d} G et {nb_C:d} C, "

8 ... f"soit un %GC de {perc_GC:.2f} %")

9 Ce génome contient 4500 G et 2575 C, soit un %GC de 47.80 %

Les instructions étant longues dans cet exemple, nous avons coupé chaque chaîne de caractères sur deux lignes. Il faut mettre à chaque fois le `f` pour préciser à Python qu'on utilise une *f-string*. Les ... indiquent que l'interpréteur attend que l'on ferme la parenthèse du `print` entamé sur la ligne précédente. Nous reverrons cette syntaxe dans le chapitre 11 *Plus sur les chaînes de caractères*.

Enfin, il est possible de préciser sur combien de caractères vous voulez qu'un résultat soit écrit et comment se fait l'alignement (à gauche, à droite), ou si vous voulez centrer le texte. Dans la portion de code suivante, le caractère `;` sert de séparateur entre les instructions sur une même ligne :

1 >>> print(10) ; print(1000)
2 10
3 1000
4 >>> print(f"{10:>6d}") ; print(f"{1000:>6d}")
5 10
6 1000
7 >>> print(f"{10:<6d}") ; print(f"{1000:<6d}")
8 10
9 1000
10 >>> print(f"{10:^6d}") ; print(f"{1000:^6d}")
11 10
12 1000
13 >>> print(f"{10:*^6d}") ; print(f"{1000:*^6d}")
14 **10**
15 *1000*
16 >>> print(f"{10:0>6d}") ; print(f"{1000:0>6d}")
17 000010
18 001000

Notez que `>` spécifie un alignement à droite, `<` spécifie un alignement à gauche et `^` spécifie un alignement centré. Il est également possible d'indiquer le caractère qui servira de remplissage lors des alignements (l'espace est le caractère par défaut).
Ce formatage est également possible sur des chaînes de caractères avec la lettre `s` (comme `string`):

```python
1 >>> print("atom HN") ; print("atom HDE1")
2 atom HN
3 atom HDE1
4 >>> print(f"atom {'HN':>4s}") ; print(f"atom {'HDE1':>4s}")
5 atom HN
6 atom HDE1
```

Vous voyez tout de suite l'énorme avantage de l'écriture formatée. Elle vous permet d'écrire en colonnes parfaitement alignées. Nous verrons que ceci est très pratique si l'on veut écrire les coordonnées des atomes d'une molécule au format PDB (pour en savoir plus sur ce format, reportez-vous à l'annexe A *Quelques formats de données en biologie*).

Pour les *floats*, il est possible de combiner le nombre de caractères à afficher avec le nombre de décimales :

```python
1 >>> print(f"{perc_GC:7.3f}")
2 47.804
3 >>> print(f"{perc_GC:10.3f}")
4 47.804
```

L'instruction `7.3f` signifie que l'on souhaite écrire un *float* avec 3 décimales et formaté sur 7 caractères (par défaut justifiés à droite). L'instruction `10.3f` fait la même chose sur 10 caractères. Remarquez que le séparateur décimal . compte pour un caractère. De même, si on avait un nombre négatif, le signe `-` compterait aussi pour un caractère.

### 3.2.4 Autres détails sur les f-strings

Si on veut afficher des accolades littérales avec les *f-strings*, il faut les doubler pour échapper au formatage :

```python
1 >>> print(f"Accolades littérales {{}} ou {{ ou }} "
2 ... f"et pour le formatage {10}")
3 Accolades littérales {} ou { ou } et pour le formatage 10
```

Une remarque importante, si on ne met pas de variable à formater entre les accolades dans une *f-string*, cela conduit à une erreur :

```python
1 >>> print(f"accolades sans variable {}")
2 File "<stdin>", line 1
3 SyntaxError: f-string: empty expression not allowed
```

Enfin, il est important de bien comprendre qu'une *f-string* est indépendante de la fonction `print()`. Si on donne une *f-string* à la fonction `print()`, Python évalue d'abord la *f-string* et c'est la chaîne de caractères qui en résulte qui est affichée à l'écran. Tout comme dans l'instruction `print(5*5)`, c'est d'abord la multiplication `(5*5)` qui est évaluée, puis son résultat qui est affiché à l'écran. On peut s'en rendre compte de la manière suivante dans l'interpréteur :

```python
1 >>> f"{perc_GC:10.3f}"  
2 ' 47.804'  
3 >>> type(f"{perc_GC:10.3f}")  
4 <class 'str'>
```

Python considère le résultat de l'instruction `f"{perc_GC:10.3f}"` comme une chaîne de caractères et la fonction `type()` nous le confirme.

### 3.2.5 Expressions dans les f-strings

Une fonctionnalité extrêmement puissante des *f-strings* est de supporter des expressions Python au sein des accolades. Ainsi, il est possible d'y mettre directement une opération ou encore un appel à une fonction :

```python
1 >>> print(f"Le résultat de 5 * 5 vaut {5 * 5}")
2 Le résultat de 5 * 5 vaut 25
3 >>> print(f"Résultat d'une opération avec des floats : {(4.1 * 6.7)}")
4 Résultat d'une opération avec des floats : 27.47
5 >>> print(f"Le minimum est {min(1, -2, 4)}")
6 Le minimum est -2
7 >>> entier = 2
8 >>> print(f"Le type de {entier} est {type(entier)}")
9 Le type de 2 est <class 'int'>
```
Nous aurons l'occasion de revenir sur cette fonctionnalité au fur et à mesure de ce cours.

Les possibilités offertes par les *f-strings* sont nombreuses. Pour vous y retrouver dans les différentes options de formatage, nous vous conseillons de consulter ce mémo<fnref n="1" /> (en anglais).

## 3.3 Écriture scientifique

Pour les nombres très grands ou très petits, l'écriture formatée permet d'afficher un nombre en notation scientifique (sous forme de puissance de 10) avec la lettre e :

1 >>> print(f"{1_000_000_000:e}")

2 1.000000e+09

3 >>> print(f"{0.000_000_001:e}")

4 1.000000e-09

Il est également possible de définir le nombre de chiffres après la virgule. Dans l'exemple ci-dessous, on affiche un nombre avec aucun, 3 et 6 chiffres après la virgule :

1 >>> avogadro_number = 6.022_140_76e23

2 >>> print(f"{avogadro_number:.0e}")

3 6e+23

4 >>> print(f"{avogadro_number:.3e}")

5 6.022e+23

6 >>> print(f"{avogadro_number:.6e}")

7 6.022141e+23

## 3.4 Exercices

### Conseil

Pour les exercices 2 à 6, utilisez l'interpréteur Python.

### 3.4.1 Affichage dans l'interpréteur et dans un programme

Ouvrez l'interpréteur Python et tapez l'instruction `1+1`. Que se passe-t-il ?

Écrivez la même chose dans un script `test.py` que vous allez créer avec un éditeur de texte. Exécutez ce script en tapant `python test.py` dans un shell. Que se passe-t-il ? Pourquoi ? Faites en sorte d'afficher le résultat de l'addition `1+1` en exécutant le script dans un shell.

### 3.4.2 Poly-A

Générez une chaîne de caractères représentant un brin d'ADN poly-A (c'est-à-dire qui ne contient que des bases A) de 20 bases de longueur, sans taper littéralement toutes les bases.

### 3.4.3 Poly-A et poly-GC

Sur le modèle de l'exercice précédent, générez en une ligne de code un brin d'ADN poly-A (AAAA...) de 20 bases suivi d'un poly-GC régulier (GCGCGC...) de 40 bases.

### 3.4.4 Écriture formatée

En utilisant l'écriture formatée, affichez en une seule ligne les variables `a`, `b` et `c` dont les valeurs sont respectivement la chaîne de caractères `"salut"`, le nombre entier `102` et le `float 10.318`. La variable `c` sera affichée avec deux décimales.

### 3.4.5 Écriture formatée 2

Dans un script `percGC.py`, calculez un pourcentage de GC avec l'instruction suivante :

perc_GC = ((4500 + 2575)/14800)*100

Ensuite, affichez le contenu de la variable `perc_GC` à l'écran avec 0, 1, 2 puis 3 décimales sous forme arrondie en utilisant l'écriture formatée et les *f-strings*. On souhaite que le programme affiche la sortie suivante :

<footnote n="1">1. https://fstring.help/cheat/</footnote>
Le pourcentage de GC est 48 %

Le pourcentage de GC est 47.8 %

Le pourcentage de GC est 47.80 %

Le pourcentage de GC est 47.804 %

### 3.4.6 Décomposition de fractions

Utilisez l'opérateur modulo (%) et l'opérateur division entière (//) pour simplifier des fractions, connaissant leur numérateur et leur dénominateur, et afficher le résultat avec des *f-strings*.

Par exemple pour la fraction $\frac{7}{3}$, le numérateur vaut 7 et le dénominateur vaut 3, et le résultat s'affichera sous la forme :

$$ \frac{7}{3} = 2 + \frac{1}{3} $$

Ici, 2 est le résultat de la division entière du numérateur par le dénominateur et 1 est le reste de la division entière du numérateur par le dénominateur.

Faites de même pour les fractions suivantes :

$$ \frac{9}{4}, \frac{23}{5}, \frac{21}{8} \text{ et } \frac{7}{2} $$
# CHAPITRE 4

## Listes

### 4.1 Définition

**Définition**

Une **liste** est une structure de données qui contient une collection d'objets Python. Il s'agit d'un nouveau type par rapport aux entiers, *float*, booléens et chaînes de caractères que nous avons vus jusqu'à maintenant. On parle aussi d'**objet séquentiel** en ce sens qu'il contient une séquence d'autres objets.

Python autorise la construction de liste contenant des valeurs de types différents (par exemple entier et chaîne de caractères), ce qui leur confère une grande flexibilité. Une liste est déclarée par une série de **valeurs** (n'oubliez pas les guillemets, simples ou doubles, s'il s'agit de chaînes de caractères) séparées par des **virgules**, et le tout encadré par des **crochets**. En voici quelques exemples :

1 >>> animaux = ["girafe", "tigre", "singe", "souris"]

2 >>> tailles = [5, 2.5, 1.75, 0.15]

3 >>> mixte = ["girafe", 5, "souris", 0.15]

4 >>> animaux

5 ['girafe', 'tigre', 'singe', 'souris']

6 >>> tailles

7 [5, 2.5, 1.75, 0.15]

8 >>> mixte

9 ['girafe', 5, 'souris', 0.15]

Lorsque l'on affiche une liste, Python la restitue telle qu'elle a été saisie.

### 4.2 Utilisation

Un des gros avantages d'une liste est que vous accédez à ses éléments par leur position. Ce numéro est appelé **indice** (ou *index*) de la liste.

liste : ["girafe", "tigre", "singe", "souris"]

indice : 0 1 2 3

Soyez très **attentif** au fait que les indices d'une liste de *n* éléments commencent à 0 et se terminent à *n* – 1. Voyez l'exemple suivant :
1 >>> animaux = ["girafe", "tigre", "singe", "souris"]

2 >>> animaux[0]

3 'girafe'

4 >>> animaux[1]

5 'tigre'

6 >>> animaux[3]

7 'souris'

Par conséquent, si on appelle l'élément d'indice 4 de notre liste, Python renverra un message d'erreur :

1 >>> animaux[4]

2 Traceback (innermost last):

3 File "<stdin>", line 1, in ?

4 IndexError: list index out of range

N'oubliez pas ceci ou vous risquez d'obtenir des bugs inattendus !

## 4.3 Opération sur les listes

Tout comme les chaînes de caractères, les listes supportent l'opérateur `+` de concaténation, ainsi que l'opérateur `*` pour la duplication :

1 >>> ani1 = ["girafe", "tigre"]

2 >>> ani2 = ["singe", "souris"]

3 >>> ani1 + ani2

4 ['girafe', 'tigre', 'singe', 'souris']

5 >>> ani1 * 3

6 ['girafe', 'tigre', 'girafe', 'tigre', 'girafe', 'tigre']

L'opérateur `+` est très pratique pour concatenérez deux listes.

Vous pouvez aussi utiliser la méthode `append()` lorsque vous souhaitez ajouter un seul élément à la fin d'une liste.

### Remarque

La notion de méthode est introduite dans la rubrique *Note sur le vocabulaire et la syntaxe* à la fin de ce chapitre.

Dans l'exemple suivant, nous allons créer une liste vide :

1 >>> liste1 = []

2 >>> liste1

3 []

puis lui ajouter deux éléments, l'un après l'autre, d'abord avec la concaténation :

1 >>> liste1 = liste1 + [15]

2 >>> liste1

3 [15]

4 >>> liste1 = liste1 + [-5]

5 >>> liste1

6 [15, -5]

puis avec la méthode `append()` :

1 >>> liste1.append(13)

2 >>> liste1

3 [15, -5, 13]

4 >>> liste1.append(-3)

5 >>> liste1

6 [15, -5, 13, -3]

Dans cet exemple, nous ajoutons des éléments à une liste en utilisant l'opérateur de concaténation `+` ou la méthode `append()`.

### Conseil
Nous vous conseillons dans ce cas précis d'utiliser la méthode `append()`, dont la syntaxe est plus élégante.

Nous reverrons en détail la méthode `append()` dans le chapitre 12 *Plus sur les listes*.

## 4.4 Indiçage négatif

La liste peut également être indexée avec des nombres négatifs selon le modèle suivant :

```python
liste : ["girafe", "tigre", "singe", "souris"]
indice positif : 0 1 2 3
indice négatif : -4 -3 -2 -1
```

ou encore :

```python
liste : ["A", "B", "C", "D", "E", "F"]
indice positif : 0 1 2 3 4 5
indice négatif : -6 -5 -4 -3 -2 -1
```

Les indices négatifs reviennent à compter à partir de la fin. Leur principal avantage est que vous pouvez accéder au dernier élément d'une liste à l'aide de l'indice `-1` sans pour autant connaître la longueur de cette liste. L'avant-dernier élément a lui l'indice `-2`, l'avant-avant dernier l'indice `-3`, etc. :

```python
1 >>> animaux = ["girafe", "tigre", "singe", "souris"]
2 >>> animaux[-1]
3 'souris'
4 >>> animaux[-2]
5 'singe'
```

Pour accéder au premier élément de la liste avec un indice négatif, il faut par contre connaître le bon indice :

```python
1 >>> animaux[-4]
2 'girafe'
```

Dans ce cas, on utilise plutôt `animaux[0]`.

## 4.5 Tranches

Un autre avantage des listes est la possibilité de sélectionner une partie d'une liste en utilisant un indiçage construit sur le modèle `[m:n+1]` pour récupérer tous les éléments, du émième au énième (de l'élément `m` inclu à l'élément `n+1` exclu). On dit alors qu'on récupère une **tranche** de la liste, par exemple :

```python
1 >>> animaux = ["girafe", "tigre", "singe", "souris"]
2 >>> animaux[0:2]
3 ['girafe', 'tigre']
4 >>> animaux[0:3]
5 ['girafe', 'tigre', 'singe']
6 >>> animaux[0:]
7 ['girafe', 'tigre', 'singe', 'souris']
8 >>> animaux[:]
9 ['girafe', 'tigre', 'singe', 'souris']
10 >>> animaux[1:]
11 ['tigre', 'singe', 'souris']
12 >>> animaux[1:-1]
13 ['tigre', 'singe']
```

Notez que lorsqu'aucun indice n'est indiqué à gauche ou à droite du symbole deux-points `:` , Python prend par défaut tous les éléments depuis le début ou tous les éléments jusqu'à la fin respectivement.

On peut aussi préciser le pas en ajoutant un symbole deux-points supplémentaire et en indiquant le pas par un entier :
```python
1 >>> animaux = ["girafe", "tigre", "singe", "souris"]
2 >>> animaux[0:3:2]
3 ['girafe', 'singe']
4 >>> x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5 >>> x
6 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
7 >>> x[:1]
8 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
9 >>> x[:2]
10 [0, 2, 4, 6, 8]
11 >>> x[:3]
12 [0, 3, 6, 9]
13 >>> x[1:6:3]
14 [1, 4]
```

Finalement, on se rend compte que l'accès au contenu d'une liste fonctionne sur le modèle `liste[début:fin:pas]`.

## 4.6 Fonction len()

L'instruction `len()` vous permet de connaître la longueur d'une liste, c'est-à-dire le nombre d'éléments que contient la liste. Voici un exemple d'utilisation :

```python
1 >>> animaux = ["girafe", "tigre", "singe", "souris"]
2 >>> len(animaux)
3 4
4 >>> len([1, 2, 3, 4, 5, 6, 7, 8])
5 8
```

## 4.7 Les fonctions range() et list()

L'instruction `range()` est une fonction spéciale en Python qui génère des nombres entiers compris dans un intervalle. Lorsqu'elle est utilisée en combinaison avec la fonction `list()`, on obtient une liste d'entiers. Par exemple :

```python
1 >>> list(range(10))
2 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```

La commande `list(range(10))` a généré une liste contenant tous les nombres entiers de 0 inclus à 10 **exclu**. Nous verrons l'utilisation de la fonction `range()` toute seule dans le chapitre 5 *Boucles et comparaisons*.

Dans l'exemple ci-dessus, la fonction `range()` a pris un argument, mais elle peut également prendre deux ou trois arguments, voyez plutôt :

```python
1 >>> list(range(0, 5))
2 [0, 1, 2, 3, 4]
3 >>> list(range(15, 20))
4 [15, 16, 17, 18, 19]
5 >>> list(range(0, 1000, 200))
6 [0, 200, 400, 600, 800]
7 >>> list(range(2, -2, -1))
8 [2, 1, 0, -1]
```

L'instruction `range()` fonctionne sur le modèle `range([début,] fin[, pas])`. Les arguments entre crochets sont optionnels. Pour obtenir une liste de nombres entiers, il faut l'utiliser systématiquement avec la fonction `list()`.

Enfin, prenez garde aux arguments optionnels par défaut `(0` pour début et `1` pour pas) :

```python
1 >>> list(range(10,0))
2 []
```

Ici la liste est vide car Python a pris la valeur du pas par défaut qui est de 1. Ainsi, si on commence à 10 et qu'on avance par pas de 1, on ne pourra jamais atteindre 0. Python génère ainsi une liste vide. Pour éviter ça, il faudrait, par exemple, préciser un pas de -1 pour obtenir une liste d'entiers décroissants :

```python
1 >>> list(range(10,0,-1))
2 [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
```
## 4.8 Listes de listes

Pour finir, sachez qu'il est tout à fait possible de construire des listes de listes. Cette fonctionnalité peut parfois être très pratique. Par exemple :

```python
1 >>> prairie1 = ["girafe", 4]
2 >>> prairie2 = ["tigre", 2]
3 >>> prairie3 = ["singe", 5]
4 >>> savane = [prairie1, prairie2, prairie3]
5 >>> savane
6 [('girafe', 4), ('tigre', 2), ('singe', 5)]
```

Dans cet exemple, chaque sous-liste contient une catégorie d'animal et le nombre d'animaux pour chaque catégorie.

Pour accéder à un élément de la liste, on utilise l'indécage habituel :

```python
1 >>> savane[1]
2 ['tigre', 2]
```

Pour accéder à un élément de la sous-liste, on utilise un double indécage :

```python
1 >>> savane[1][0]
2 'tigre'
3 >>> savane[1][1]
4 2
```

On verra un peu plus loin qu'il existe en Python des dictionnaires qui sont également très pratiques pour stocker de l'information structurée. On verra aussi qu'il existe un module nommé `NumPy` qui permet de créer des listes ou des tableaux de nombres (vecteurs et matrices) et de les manipuler.

## 4.9 Minimum, maximum et somme d'une liste

Les fonctions `min()`, `max()` et `sum()` renvoient respectivement le minimum, le maximum et la somme d'une liste passée en argument :

```python
1 >>> liste1 = list(range(10))
2 >>> liste1
3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4 >>> sum(liste1)
5 45
6 >>> min(liste1)
7 0
8 >>> max(liste1)
9 9
```

Même si en théorie ces fonctions peuvent prendre en argument une liste de *strings*, on les utilisera la plupart du temps avec des types numériques (liste d'entiers et / ou de *floats*).

Nous avions déjà croisé `min()`, `max()` dans le chapitre 2 *Variables*. Ces deux fonctions pouvaient prendre plusieurs arguments entiers et / ou *floats*, par exemple :

```python
1 >>> min(3, 4)
2 3
```

Attention toutefois à ne pas mélanger entiers et *floats* d'une part avec une liste d'autre part, car cela renvoie une erreur :

```python
1 >>> min(liste1, 3, 4)
2 Traceback (most recent call last):
3     File "<stdin>", line 1, in <module>
4 TypeError: '<' not supported between instances of 'int' and 'list'
```

Soit on passe plusieurs entiers et / ou *floats* en argument, soit on passe une liste unique.

## 4.10 Problème avec les copies de listes

Nous attirons votre attention sur un comportement de Python qui peut paraître étrange lorsqu'on copie une liste :
```python
1 >>> liste1 = list(range(5))
2 >>> list(range(5))
3 >>> liste1
4 [0, 1, 2, 3, 4]
5 >>> liste2 = liste1
6 >>> liste2
7 [0, 1, 2, 3, 4]
8 >>> liste1[3] = -50
9 >>> liste1
10 [0, 1, 2, -50, 4]
11 >>> liste2
12 [0, 1, 2, -50, 4]
```

Comme vous voyez en ligne 8, la modification de `liste1` a modifié également `liste2`. Cela vient du fait que Python a effectué la copie de liste en ligne 5 par référence. Ainsi, les deux listes pointent vers le même objet dans la mémoire. Pour contrer ce problème et faire en sorte que `liste2` soit bien distincte de `liste1`, on peut utiliser la fonction `list()` :

```python
1 >>> liste1 = list(range(5))
2 >>> liste1
3 [0, 1, 2, 3, 4]
4 >>> liste2 = list(liste1)
5 >>> liste2
6 [0, 1, 2, 3, 4]
7 >>> liste1[3] = -50
8 >>> liste1
9 [0, 1, 2, -50, 4]
10 >>> liste2
11 [0, 1, 2, 3, 4]
```

### Attention

Cette astuce ne fonctionne que pour des listes à une dimension (c'est-à-dire pour des listes qui ne contiennent que des éléments de type simple comme des entiers, des floats, des chaînes de caractères et des booléens), mais pas pour des listes de listes. Le chapitre 12 *Plus sur les listes* explique l'origine de ce comportement et comment s'en sortir à tous les coups.

## 4.11 Note sur le vocabulaire et la syntaxe

Revenons quelques instants sur la notion de **méthode** abordée dans ce chapitre avec `.append()`. En Python, on peut considérer chaque variable comme un objet sur lequel on peut appliquer des méthodes. Une méthode est simplement une fonction qui utilise et/ou agit sur l'objet lui-même, les deux étant connectés par un point. La syntaxe générale est de la forme `objet.méthode()`.

Dans l'exemple suivant :

```python
1 >>> liste1 = [1, 2]
2 >>> liste1.append(3)
3 >>> liste1
4 [1, 2, 3]
```

la méthode `.append()` est liée à `liste1` qui est un objet de type `liste`. La méthode modifie l'objet liste en lui ajoutant un élément.

Nous aurons de nombreuses occasions de revoir cette notation `objet.méthode()`.

## 4.12 Exercices

### Conseil

Pour ces exercices, utilisez l'interpréteur Python.
### 4.12.1 Prédire la sortie

Soit les trois lignes de code suivantes :

```python
1 liste1 = list(range(10, 15))
```

```python
2 var = 0
```

```python
3 var2 = 10
```

Prédisez le comportement de chaque instruction ci-dessous, sans les recopier dans un script ni dans l'interpréteur Python :

* `print(liste1[2])`

* `print(liste1(var))`

* `print(liste1(var2))`

* `print(liste1("var"))`

Lorsqu'une instruction produit une erreur, identifiez pourquoi.

### 4.12.2 Jours de la semaine

Constituez une liste `semaine` contenant les sept jours de la semaine.

1. À partir de cette liste, comment récupérez-vous seulement les cinq premiers jours de la semaine d'une part, et ceux du week-end d'autre part ? Utilisez pour cela l'indiçage.

2. Cherchez un autre moyen pour arriver au même résultat (en utilisant un autre indiçage).

3. Trouvez deux manières pour accéder au dernier jour de la semaine.

4. Inversez les jours de la semaine en une commande.

### 4.12.3 Saisons

Créez quatre listes `hiver`, `printemps`, `ete` et `automne` contenant les mois correspondants à ces saisons. Créez ensuite une liste `saisons` contenant les listes `hiver`, `printemps`, `ete` et `automne`. Prévoyez ce que renvoient les instructions suivantes, puis vérifiez-le dans l'interpréteur :

1. `saisons[2]`

2. `saisons[1][0]`

3. `saisons[1:2]`

4. `saisons[:][1]`. Comment expliquez-vous ce dernier résultat ?

### 4.12.4 Table de multiplication par 9

Affichez la table de multiplication par 9 en une seule commande avec les instructions `range()` et `list()`.

### 4.12.5 Nombres pairs

Répondez à la question suivante en une seule commande. Combien y a-t-il de nombres pairs dans l'intervalle `[2, 10000]` inclus ?
# Boucles et comparaisons

## 5.1 Boucles for

### 5.1.1 Principe

En programmation, on est souvent amené à répéter plusieurs fois une instruction. Incontournables à tout langage de programmation, les boucles vont nous aider à réaliser cette tâche répétitive de manière compacte et efficace.

Imaginez par exemple que vous souhaitiez afficher les éléments d'une liste les uns après les autres. Dans l'état actuel de vos connaissances, il faudrait taper quelque chose du style :

1 animaux = ["girafe", "tigre", "singe", "souris"]

2 print(animaux[0])

3 print(animaux[1])

4 print(animaux[2])

5 print(animaux[3])

Si votre liste ne contient que 4 éléments, ceci est encore faisable mais imaginez qu'elle en contienne 100 voire 1 000 !
Pour remédier à cela, il faut utiliser les boucles. Regardez l'exemple suivant :

1 >>> animaux = ["girafe", "tigre", "singe", "souris"]

2 >>> for animal in animaux:

3 ... print(animal)

4 ...

5 girafe

6 tigre

7 singe

8 souris

Commentons en détails ce qu'il s'est passé dans cet exemple :

La variable *animal* est appelée **variable d'itération**, elle prend successivement les différentes valeurs de la liste animaux à chaque *itérations* (ou tour) de boucle. On verra un peu plus loin dans ce chapitre que l'on peut choisir le nom que l'on veut pour cette variable. Celle-ci est créée par Python la première fois que la ligne contenant le *for* est exécutée (si elle existait déjà son contenu serait écrasé). Une fois la boucle terminée, cette variable d'itération *animal* n'est pas détruite et conserve la dernière valeur de la liste animaux (ici la chaîne de caractères *souris*).

Notez bien les types des variables utilisées ici :

* animaux est une **liste** sur laquelle on itère;

* *animal* est une **chaîne de caractères** car chaque élément de la liste *animaux* est une chaîne de caractères.
Nous verrons plus loin que la variable d'itération peut être de n'importe quel type selon la liste parcourue. En Python, une boucle itère la plupart du temps sur un objet dit **séquentiel** (c'est-à-dire un objet constitué d'autres objets) tel qu'une liste. De tels objets sont dits **itérables** car on peut effectuer une boucle dessus. Nous verrons aussi plus tard d'autres objets séquentiels sur lesquels on peut itérer dans une boucle.

D'ores et déjà, pretez attention au caractère **deux-points** « : » à la fin de la ligne débutant par **for**. Cela signifie que la boucle **for** attend un **bloc d'instructions**, en l'occurrence toutes les instructions que Python répétera à chaque itération de la boucle. On appelle ce bloc d'instructions le **corps de la boucle**. Comment indique-t-on à Python où ce bloc commence et se termine ? Cela est signalé uniquement par l'**indentation**, c'est-à-dire le décalage vers la droite de la (ou des) **ligne(s)** du bloc d'instructions.

Remarque

Les notions de bloc d'instruction et d'indentations ont été introduites dans le chapitre 1 *Introduction*.

Dans l'exemple suivant, le corps de la boucle contient deux instructions (ligne 2 et ligne 3) car elles sont indentées par rapport à la ligne débutant par **for** :

1 for animal in animaux:
2     print(animal)
3     print(animal*2)
4 print("C'est fini")

La ligne 4 ne fait pas partie du corps de la boucle car elle est au même niveau que le **for** (c'est-à-dire non indentée par rapport au **for**). Notez également que chaque instruction du corps de la boucle doit être indentée de la même manière (ici 4 espaces).

Remarque

Outre une meilleure lisibilité, les deux-points et l'**indentation** sont formellement requis en Python. Même si on peut indenter comme on veut (plusieurs espaces ou plusieurs tabulations, mais pas une combinaison des deux), les développeurs recommandent l'utilisation de quatre espaces. Vous pouvez consulter à ce sujet le chapitre 16 *Bonnes pratiques de programmation* en Python.

Faites en sorte de configurer votre éditeur de texte favori de façon à écrire quatre espaces lorsque vous tapez sur la touche **Tab** (tabulation).

Si on oublie l'indentation, Python renvoie un message d'erreur :

1 >>> for animal in animaux:
2     ... print(animal)
3         File "<stdin>", line 2
4             print(animal)
5             ^
6 IndentationError: expected an indented block

Dans les exemples ci-dessus, nous avons exécuté une boucle en itérant directement sur une liste. Une tranche d'une liste étant elle même une liste, on peut également itérer dessus :

1 >>> animaux = ["girafe", "tigre", "singe", "souris"]
2 >>> for animal in animaux[1:3]:
3     ... print(animal)
4 ...
5 tigre
6 singe

On a vu que les boucles **for** pouvaient utiliser une liste contenant des chaînes de caractères, mais elles peuvent tout aussi bien utiliser des listes contenant des entiers (ou n'importe quel type de variable) :
```python
1 >>> for i in [1, 2, 3]:
2     ...     print(i)
3     ...
4 1
5 2
6 3
```

## 5.1.2 Fonction range()

Python possède la fonction `range()` que nous avons rencontrée précédemment dans le chapitre 4 *Listes*, pratique pour faire une boucle sur une liste d'entiers de manière automatique :

```python
1 >>> for i in range(4):
2     ...     print(i)
3     ...
4 0
5 1
6 2
7 3
```

Dans cet exemple, nous pouvons faire plusieurs remarques importantes :

* Contrairement à la création de liste avec `list(range(4))`, la fonction `range()` peut être utilisée telle quelle dans une boucle. Il n'est pas nécessaire de taper `for i in list(range(4))`: même si cela fonctionnerait également.

* Comment cela est possible ? `range()` est une fonction qui a été spécialement conçue pour cela<fnref n="1" />, c'est-à-dire que l'on peut itérer directement dessus. Pour Python, il s'agit d'un nouveau type : par exemple dans l'instruction `x = range(3)`, la variable `x` est de type `range` (tout comme on avait les types `int`, `float`, `str` ou `list`) à utiliser spécialement avec les boucles.

* L'instruction `list(range(4))` se contente de transformer un objet de type `range` en un objet de type `list`. Si vous vous souvenez bien, il s'agit d'une fonction de *casting*, qui convertit un type en un autre (voir chapitre 2 *Variables*). Il n'y aucun intérêt à utiliser dans une boucle la construction `for i in list(range(4))`: C'est même contre-productif. En effet, `range()` se contente de stocker l'entier actuel, le pas pour passer à l'entier suivant, et le dernier entier à parcourir, ce qui revient à stocker seulement 3 nombres entiers et ce quelle que soit la longueur de la séquence, même avec un `range(1000000)`. Si on utilisait `list(range(1000000))`, Python construirait d'abord une liste de 1 million d'éléments dans la mémoire puis itérerait dessus, d'où une énorme perte de temps !

## 5.1.3 Nommage de la variable d'itération

Dans l'exemple précédent, nous avons choisi le nom `i` pour la variable d'itération. Ceci est une habitude en informatique et indique en général qu'il s'agit d'un entier (le nom `i` vient sans doute du mot indice ou *index* en anglais). Nous vous conseillons de suivre cette convention afin d'éviter les confusions. Si vous itérez sur les indices, vous pouvez appeler la variable d'itération `i` (par exemple dans `for i in range(4):`).

Si, par contre, vous itérez sur une liste comportant des chaînes de caractères (ou tout autre type de variable), utilisez un nom explicite pour la variable d'itération. Par exemple :

```python
for prenom in ["Joe", "Bill", "John"]:
    ou
    for proportion in [0.12, 0.53, 0.07, 0.28]:
```

## 5.1.4 Itération sur les indices ou les éléments

Revenons à notre liste `animaux`. Nous allons maintenant parcourir cette liste, mais cette fois par une itération sur ses indices :

```python
1 >>> animaux = ["girafe", "tigre", "singe", "souris"]
2 >>> for i in range(4):
3     ...     print(animaux[i])
4 ...
5 girafe
6 tigre
7 singe
8 souris
```

<footnote n="1">1. https://docs.python.org/fr/3/library/stdtypes.html#typeseq-range</footnote>
La variable *i* prendra les valeurs successives 0, 1, 2 et 3 et on accèdera à chaque élément de la liste animaux par son indice (*i*.*i*). Notez à nouveau le nom *i* de la variable d'itération car on itère sur les indices.

Quand utiliser l'une ou l'autre des deux méthodes ? La plus efficace est celle qui **réalise les itérations directement sur les éléments** :

1 >>> animaux = ["girafe", "tigre", "singe", "souris"]
2 >>> for animal in animaux:
3 ...     print(animal)
4 ...
5 girafe
6 tigre
7 singe
8 souris

Remarque

Dans le chapitre 18 *Jupyter et ses notebooks*, nous mesurerons le temps d'exécution de ces deux méthodes pour vous montrer que l'itération sur les éléments est la méthode la plus rapide.

Toutefois, il se peut qu'au cours d'une boucle vous ayez besoin des indices, auquel cas vous devrez itérer sur les indices :

1 >>> animaux = ["girafe", "tigre", "singe", "souris"]
2 >>> for i in range(len(animaux)):
3 ...     print(f"L'animal {i} est un(e) {animaux[i]}")
4 ...
5 L'animal 0 est un(e) girafe
6 L'animal 1 est un(e) tigre
7 L'animal 2 est un(e) singe
8 L'animal 3 est un(e) souris

Enfin, Python possède la fonction **enumerate()** qui vous permet d'itérer sur les indices et les éléments eux-mêmes :

1 >>> animaux = ["girafe", "tigre", "singe", "souris"]
2 >>> for i, animal in enumerate(animaux):
3 ...     print(f"L'animal {i} est un(e) {animal}")
4 ...
5 L'animal 0 est un(e) girafe
6 L'animal 1 est un(e) tigre
7 L'animal 2 est un(e) singe
8 L'animal 3 est un(e) souris

5.2 Comparaisons

Avant de passer aux boucles while, abordons tout de suite les **comparaisons**. Celles-ci seront reprises dans le chapitre 6 *Tests*.

Python est capable d'effectuer toute une série de comparaisons entre le contenu de deux variables, telles que :

<table><thead><tr><td>Opérateur de comparaison</td><td>Signification</td></tr></thead><tbody><tr><td>==</td><td>égal à</td></tr><tr><td>!=</td><td>différent de</td></tr><tr><td>&gt;</td><td>strictement supérieur à</td></tr><tr><td>&gt;=</td><td>supérieur ou égal à</td></tr><tr><td>&lt;</td><td>strictement inférieur à</td></tr><tr><td>&lt;=</td><td>inférieur ou égal à</td></tr></tbody></table>

Observez les exemples suivants avec des nombres entiers :
1 >>> x = 5

2 >>> x == 5

3 True

4 >>> x > 10

5 False

6 >>> x < 10

7 True

Python renvoie la valeur `True` si la comparaison est vraie et `False` si elle est fausse. `True` et `False` sont des booléens comme nous avions vu au chapitre 2 *Variables*.

Faites bien attention à ne pas confondre l'**opérateur d'affectation** `=` qui affecte une valeur à une variable et l'**opérateur de comparaison** `==` qui compare les valeurs de deux variables.

Vous pouvez également effectuer des comparaisons sur des chaînes de caractères.

1 >>> animal = "tigre"

2 >>> animal == "tig"

3 False

4 >>> animal != "tig"

5 True

6 >>> animal == "tigre"

7 True

Dans le cas des chaînes de caractères, *a priori* seuls les tests `==` et `!=` ont un sens. En fait, on peut aussi utiliser les opérateurs `<`, `>`, `<=` et `>=`. Dans ce cas, l'ordre alphabétique est pris en compte, par exemple :

1 >>> "a" < "b"

2 True

"a" est **inférieur** à "b" car le caractère *a* est situé avant le caractère *b* dans l'ordre alphabétique. En fait, c'est l'ordre ASCII<fnref n="2" /> des caractères qui est pris en compte (à chaque caractère correspond un code numérique), on peut donc aussi comparer des caractères spéciaux (comme `#` ou `~`) entre eux. Enfin, on peut comparer des chaînes de caractères de plusieurs caractères :

1 >>> "ali" < "alo"

2 True

3 >>> "abb" < "ada"

4 True

Dans ce cas, Python compare les deux chaînes de caractères, caractère par caractère, de la gauche vers la droite (le premier caractère avec le premier, le deuxième avec le deuxième, etc). Dès qu'un caractère est différent entre l'une et l'autre des deux chaînes, il considère que la chaîne la plus petite est celle qui présente le caractère ayant le plus petit code ASCII (les caractères suivants de la chaîne de caractères sont ignorés dans la comparaison), comme dans l'exemple `"abb" < "ada"` ci-dessus.

## 5.3 Boucles while

Une alternative à l'instruction `for` couramment utilisée en informatique est la boucle `while`. Avec ce type de boucle, une série d'instructions est exécutée tant qu'une condition est vraie. Par exemple :

1 >>> i = 1

2 >>> while i <= 4:

3 ... print(i)

4 ... i = i + 1

5 ...

6 1

7 2

8 3

9 4

Remarquez qu'il est encore une fois nécessaire d'indenter le bloc d'instructions correspondant au corps de la boucle (ici, les instructions lignes 3 et 4).

<footnote n="2">2. http://fr.wikipedia.org/wiki/American_Standard_Code_for_Information_Interchange</footnote>
<figure><img src="image_6.png" /></figure>

### 5.4.10 Pyramide

Créez un script `pyra.py` qui dessine une pyramide comme celle-ci :

<figure><img src="image_7.png" /></figure>

Essayez de faire évoluer votre script pour dessiner la pyramide à partir d'un nombre arbitraire de lignes N. Vous pourrez demander à l'utilisateur le nombre de lignes de la pyramide avec les instructions suivantes qui utilisent la fonction `input()` :

1 reponse = input("Entrez un nombre de lignes (entier positif): ")

2 N = int(reponse)

### 5.4.11 Parcours de matrice

Imaginons que l'on souhaite parcourir tous les éléments d'une matrice carrée, c'est-à-dire d'une matrice qui est constituée d'autant de lignes que de colonnes.

Créez un script qui parcourt chaque élément de la matrice et qui affiche le numéro de ligne et de colonne uniquement avec des boucles `for`.

Pour une matrice de dimensions $2 \times 2$, le schéma de la figure 5.1 vous indique comment parcourir une telle matrice. L'affichage attendu est :

ligne colonne

1 1

1 2

2 1

2 2

Attention à bien respecter l'alignement des chiffres qui doit être justifié à droite sur 4 caractères. Testez avec une matrice de dimensions $3 \times 3$, puis $5 \times 5$, et enfin $10 \times 10$.

Créez une seconde version de votre script, cette fois-ci avec deux boucles `while`.

### 5.4.12 Parcours de demi-matrice sans la diagonale (exercice ++)

En se basant sur le script précédent, on souhaite réaliser le parcours d'une demi-matrice carrée sans la diagonale. On peut noter que cela produit tous les couples possibles une seule fois (1 et 2 est équivalent à 2 et 1), en excluant par ailleurs chaque élément avec lui même (1 et 1, 2 et 2, etc). Pour mieux comprendre ce qui est demandé, la figure 5.2 indique les cases à parcourir en gris :

Créez un script qui affiche le numéro de ligne et de colonne, puis la taille de la matrice $N \times N$ et le nombre total de cases parcourues. Par exemple pour une matrice $4 \times 4$ ($N=4$) :
<figure><img src="image_8.png" /><figcaption>FIGURE 5.1 – Parcours d'une matrice.</figcaption></figure>

<figure><img src="image_9.png" /><figcaption>FIGURE 5.2 – Demi-matrice sans la diagonale (en gris).</figcaption></figure>

ligne colonne
1 2
1 3
1 4
2 3
2 4
3 4
Pour une matrice 4x4, on a parcouru 6 cases

Testez votre script avec N=3, puis N=4 et enfin N=5.

Concevez une seconde version à partir du script précédent, où cette fois on n'affiche plus tous les couples possibles, mais simplement la valeur de N et le nombre de cases parcourues. Affichez cela pour des valeurs de N allant de 2 à 10.

Pouvez-vous trouver une formule générale reliant le nombre de cases parcourues à N ?

### 5.4.13 Sauts de puce

On imagine une puce qui se déplace aléatoirement sur une ligne, en avant ou en arrière, par pas de 1 ou -1. Par exemple, si elle est à l'emplacement 0, elle peut sauter à l'emplacement 1 ou -1; si elle est à l'emplacement 2, elle peut sauter à l'emplacement 3 ou 1, etc.

Avec une boucle *while*, simulez le mouvement de cette puce de l'emplacement initial 0 à l'emplacement final 5 (voir le schéma de la figure 5.3). Combien de sauts sont nécessaires pour réaliser ce parcours ? Relancez plusieurs fois le programme. Trouvez-vous le même nombre de sauts à chaque exécution ?

**Conseil**

Utilisez l'instruction `random.choice([-1,1])` qui renvoie au hasard les valeurs -1 ou 1 avec la même probabilité.
<figure><img src="image_10.png" /><figcaption>FIGURE 5.3 – Sauts de puce.</figcaption></figure>

Avant d'utiliser cette instruction, mettez au tout début de votre script la ligne
`import random`

Nous verrons la signification de cette syntaxe particulière dans le chapitre 9 *Modules*.

### 5.4.14 Suite de Fibonacci (exercice +++)

La suite de Fibonacci<fnref n="3" /> est une suite mathématique qui porte le nom de Leonardo Fibonacci, un mathématicien italien du XIIIe siècle. Initialement, cette suite a été conçue pour décrire la croissance d'une population de lapins, mais elle peut également être utilisée pour décrire certains motifs géométriques retrouvés dans la nature (coquillages, fleurs de tournesol...).

Pour la suite de Fibonacci ($x_n$), le terme au rang $n$ (avec $n > 1$) est la somme des nombres aux rangs $n-1$ et $n-2$ :

$$x_n = x_{n-1} + x_{n-2}$$

Par définition, les deux premiers termes sont $x_0 = 0$ et $x_1 = 1$.

À titre d'exemple, les 10 premiers termes de la suite de Fibonacci sont donc 0, 1, 1, 2, 3, 5, 8, 13, 21 et 34.

Créez un script qui construit une liste ` fibo` avec les 15 premiers termes de la suite de Fibonacci puis l'affiche.

Améliorez ce script en affichant, pour chaque élément de la liste `fibo` avec $n > 1$, le rapport entre l'élément de rang $n$ et l'élément de rang $n-1$. Ce rapport tend-il vers une constante ? Si oui, laquelle ?

<footnote n="3">3. https://fr.wikipedia.org/wiki/Suite_de_Fibonacci</footnote>
# CHAPITRE 6

## Tests

### 6.1 Définition

Les tests sont un élément essentiel à tout langage informatique si on veut lui donner un peu de complexité car ils permettent à l'ordinateur de prendre des décisions. Pour cela, Python utilise l'instruction `if` ainsi qu'une comparaison que nous avons abordée au chapitre précédent. Voici un premier exemple :

```python
1 >>> x = 2
2 >>> if x == 2:
3     ...     print("Le test est vrai !")
4     ...
5 Le test est vrai !
```

et un second :

```python
1 >>> x = "souris"
2 >>> if x == "tigre":
3     ...     print("Le test est vrai !")
4     ...
```

Il y a plusieurs remarques à faire concernant ces deux exemples :

* Dans le premier exemple, l'instruction `print("Le test est vrai !")` est exécutée, car le test est vrai. Dans le second exemple, le test est faux et rien n'est affiché.

* Les blocs d'instructions dans les tests doivent forcément être indentés comme pour les boucles `for` et `while`. L'indentation indique la portée des instructions à exécuter si le test est vrai.

* Comme avec les boucles `for` et `while`, la ligne qui contient l'instruction `if` se termine par le caractère deux-points « : ».

### 6.2 Tests à plusieurs cas

Parfois, il est pratique de tester si la condition est vraie ou si elle est fausse dans une même instruction `if`. Plutôt que d'utiliser deux instructions `if`, on peut se servir des instructions `if` et `else` :
```python
1 >>> x = 2
2 >>> if x == 2:
3     ...     print("Le test est vrai !")
4     ... else:
5     ...     print("Le test est faux !")
6     ...
7 Le test est vrai !
8 >>> x = 3
9 >>> if x == 2:
10    ...     print("Le test est vrai !")
11    ... else:
12    ...     print("Le test est faux !")
13    ...
14 Le test est faux !
```

On peut utiliser une série de tests dans la même instruction `if`, notamment pour tester plusieurs valeurs d'une même variable.

Par exemple, on se propose de tirer au sort une base d'ADN puis d'afficher le nom de cette dernière. Dans le code suivant, nous utilisons l'instruction `random.choice(liste)` qui renvoie un élément choisi au hasard dans une liste. L'instruction `import random` sera vue plus tard dans le chapitre 9 *Modules*, admettez pour le moment qu'elle est nécessaire :

```python
1 >>> import random
2 >>> base = random.choice(["a", "t", "c", "g"])
3 >>> if base == "a":
4     ...     print("choix d'une adénine")
5     ... elif base == "t":
6     ...     print("choix d'une thymine")
7     ... elif base == "c":
8     ...     print("choix d'une cytosine")
9     ... elif base == "g":
10    ...     print("choix d'une guanine")
11    ...
12 choix d'une cytosine
```

Dans cet exemple, Python teste la première condition puis, si et seulement si elle est fausse, teste la deuxième et ainsi de suite... Le code correspondant à la première condition vérifiée est exécuté puis Python sort du bloc d'instructions du `if`. Il est également possible d'ajouter une condition `else` supplémentaire qui est exécutée si aucune des conditions du `if` et des `elif` n'est vraie.

## 6.3 Importance de l'indentation

De nouveau, faites bien attention à l'indentation ! Vous devez être très rigoureux sur ce point. Pour vous en convaincre, exécutez ces deux exemples de code :

### Code 1

```python
1 nombres = [4, 5, 6]
2 for nb in nombres:
3     if nb == 5:
4         print("Le test est vrai")
5         print(f"car la variable nb vaut {nb}")
```

Résultat :

```
Le test est vrai
car la variable nb vaut 5
```
## Code 2

```python
1 nombres = [4, 5, 6]
2 for nb in nombres:
3     if nb == 5:
4         print("Le test est vrai")
5         print(f"car la variable nb vaut {nb}")
```

Résultat :

```
car la variable nb vaut 4
Le test est vrai
car la variable nb vaut 5
car la variable nb vaut 6
```

Les deux codes pourtant très similaires produisent des résultats très différents. Si vous observez avec attention l'indentation des instructions sur la ligne 5, vous remarquerez que dans le code 1, l'instruction est indentée deux fois, ce qui signifie qu'elle appartient au bloc d'instructions du test `if`. Dans le code 2, l'instruction de la ligne 5 n'est indentée qu'une seule fois, ce qui fait qu'elle n'appartient plus au bloc d'instructions du test `if`, d'où l'affichage de `car la variable nb vaut xx` pour toutes les valeurs de nb.

## 6.4 Tests multiples

Les tests multiples permettent de tester plusieurs conditions en même temps en utilisant des opérateurs booléens. Les deux opérateurs les plus couramment utilisés sont **OU** et **ET**. Voici un petit rappel sur le fonctionnement de l'opérateur **OU** :

<table><thead><tr><th>Condition 1</th><th>Opérateur</th><th>Condition 2</th><th>Résultat</th></tr></thead><tbody><tr><td>Vrai</td><td>OU</td><td>Vrai</td><td>Vrai</td></tr><tr><td>Vrai</td><td>OU</td><td>Faux</td><td>Vrai</td></tr><tr><td>Faux</td><td>OU</td><td>Vrai</td><td>Vrai</td></tr><tr><td>Faux</td><td>OU</td><td>Faux</td><td>Faux</td></tr></tbody></table>

et de l'opérateur **ET** :

<table><thead><tr><th>Condition 1</th><th>Opérateur</th><th>Condition 2</th><th>Résultat</th></tr></thead><tbody><tr><td>Vrai</td><td>ET</td><td>Vrai</td><td>Vrai</td></tr><tr><td>Vrai</td><td>ET</td><td>Faux</td><td>Faux</td></tr><tr><td>Faux</td><td>ET</td><td>Vrai</td><td>Faux</td></tr><tr><td>Faux</td><td>ET</td><td>Faux</td><td>Faux</td></tr></tbody></table>

En Python, on utilise le mot réservé `and` pour l'opérateur **ET** et le mot réservé `or` pour l'opérateur **OU**. Respectez bien la casse des opérateurs `and` et `or` qui, en Python, s'écrivent en minuscule. En voici un exemple d'utilisation :

```python
1 >>> x = 2
2 >>> y = 2
3 >>> if x == 2 and y == 2:
4     ...     print("le test est vrai")
5 ... 
6 le test est vrai
```

Notez que le même résultat serait obtenu en utilisant deux instructions `if` imbriquées :

```python
1 >>> x = 2
2 >>> y = 2
3 >>> if x == 2:
4     ...     if y == 2:
5     ...         print("le test est vrai")
6 ... 
7 le test est vrai
```
### Conseil

Nous vous conseillons la syntaxe avec un `and` qui est plus compacte. De manière générale, moins il y a de niveau d'indentations mieux c'est pour la lisibilité.

Vous pouvez aussi tester directement l'effet de ces opérateurs à l'aide de `True` et `False` (attention à respecter la casse) :

1 >>> True or False

2 True

Enfin, on peut utiliser l'opérateur logique de négation `not` qui inverse le résultat d'une condition :

1 >>> not True

2 False

3 >>> not False

4 True

5 >>> not (True and True)

6 False

## 6.5 Instructions break et continue

Ces deux instructions modifient le comportement d'une boucle (for ou while) avec un test. Ainsi, l'instruction break stoppe la boucle en cours :

1 >>> for nombre in range(4):

2 ... if nombre > 1:

3 ... break

4 ... print(name)

5 ...

6 0

7 1

L'instruction continue saute à l'itération suivante, sans exécuter la suite du bloc d'instructions de la boucle :

1 >>> for nombre in range(4):

2 ... if nombre == 2:

3 ... continue

4 ... print(name)

5 ...

6 0

7 1

8 3

## 6.6 Tests de valeur sur des floats

Lorsque l'on souhaite tester la valeur d'une variable de type `float`, le premier réflexe serait d'utiliser l'opérateur d'égalité comme :

1 >>> 1/10 == 0.1

2 True

Toutefois, nous vous le déconseillons formellement. Pourquoi ? Python stocke les valeurs numériques des `floats` sous forme de nombres flottants (d'où leur nom), et cela mène à certaines limitations<fnref n="1" />. Observez l'exemple suivant :

1 >>> (3 - 2.7) == 0.3

2 False

3 >>> 3 - 2.7

4 0.2999999999999998

Nous voyons que le résultat de l'opération `3 - 2.7` n'est pas exactement `0.3` d'où le résultat `False` en ligne 2.

En fait, ce problème ne vient pas de Python, mais plutôt de la manière dont un ordinateur traite les nombres flottants (comme un rapport de nombres binaires). Ainsi certaines valeurs de `float` ne peuvent être qu'approchées. Une manière

<footnote n="1">1. https://docs.python.org/fr/3/tutorial/floatingpoint.html</footnote>
de s'en rendre compte est d'utiliser l'écriture formatée en demandant l'affichage d'un grand nombre de décimales :

```python
1 >>> 0.3
2 0.3
3 >>> f"{0.3:.5f}"
4 '0.30000'
5 >>> f"{0.3:.60f}"
6 '0.299999999999999888977697537484345957636833190917968750000000'
7 >>> f"{3 - 2.7:.60f}"
8 '0.29999999999999822364316059974953532218933105468750000000000'
```

On observe que lorsqu'on tape `0.3`, Python affiche une valeur arrondie. En réalité, le nombre réel `0.3` ne peut être qu'approché lorsqu'on le code en nombre flottant. Il est essentiel d'avoir cela en tête lorsque l'on compare deux floats.

### Conseil

Pour les raisons évoquées ci-dessus, il ne faut surtout pas tester si un `float` est égal à une certaine valeur. La bonne pratique est de vérifier si un `float` est compris dans un intervalle avec une certaine précision. Si on appelle cette précision `delta`, on peut procéder ainsi :

```python
1 >>> delta = 0.0001
2 >>> var = 3.0 - 2.7
3 >>> 0.3 - delta < var < 0.3 + delta
4 True
5 >>> abs(var - 0.3) < delta
6 True
```

Ici on teste si `var` est compris dans l'intervalle $0,3 \pm \delta$.
Les deux méthodes mènent à un résultat strictement équivalent :

* La ligne 3 est intuitive car elle ressemble à un encadrement mathématique.

* La ligne 5 utilise la fonction valeur absolue `abs()` et est plus compacte.

## 6.7 Exercices

### Conseil

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

### 6.7.1 Jours de la semaine

Constituez une liste `semaine` contenant le nom des sept jours de la semaine.

En utilisant une boucle, écrivez chaque jour de la semaine ainsi que les messages suivants :

* Au travail s'il s'agit du lundi au jeudi;

* Chouette c'est vendredi s'il s'agit du vendredi;

* Repos ce week-end s'il s'agit du samedi ou du dimanche.

Ces messages ne sont que des suggestions, vous pouvez laisser libre cours à votre imagination.

### 6.7.2 Séquence complémentaire d'un brin d'ADN

La liste ci-dessous représente la séquence d'un brin d'ADN :

```
[A", "C", "G", "T", "T", "A", "G", "C", "T", "A", "A", "C", "G"]
```

Créez un script qui transforme cette séquence en sa séquence complémentaire.

Rappel : la séquence complémentaire s'obtient en remplaçant A par T, T par A, C par G et G par C.

### 6.7.3 Minimum d'une liste

La fonction `min()` de Python renvoie l'élément le plus petit d'une liste constituée de valeurs numériques ou de chaînes de caractères. Sans utiliser cette fonction, créez un script qui détermine le plus petit élément de la liste `[8, 4, 6, 1, 5]`.
### 6.7.4 Fréquence des acides aminés

La liste ci-dessous représente une séquence d'acides aminés :

```
["R", "A", "W", "W", "A", "W", "A", "R", "W", "W", "R", "A", "G"]
```

Calculez la fréquence des acides aminés alanine (A), arginine (R), tryptophane (W) et glycine (G) dans cette séquence.

### 6.7.5 Notes et mention d'un étudiant

Voici les notes d'un étudiant : 14, 9, 13, 15 et 12. Créez un script qui affiche la note maximum (utilisez la fonction `max()`), la note minimum (utilisez la fonction `min()`) et qui calcule la moyenne.

Affichez la valeur de la moyenne avec deux décimales. Affichez aussi la mention obtenue sachant que la mention est « passable » si la moyenne est entre 10 inclus et 12 exclus, « assez bien » entre 12 inclus et 14 exclus et « bien » au-delà de 14.

### 6.7.6 Nombres pairs

Construisez une boucle qui parcourt les nombres de 0 à 20 et qui affiche les nombres pairs inférieurs ou égaux à 10 d'une part, et les nombres impairs strictement supérieurs à 10 d'autre part.

Pour cet exercice, vous pourrez utiliser l'opérateur modulo % qui renvoie le reste de la division entière entre deux nombres et dont voici quelques exemples d'utilisation :

```
1 >>> 4 % 3
2 1
3 >>> 5 % 3
4 2
5 >>> 4 % 2
6 0
7 >>> 6 % 2
8 0
```

Vous remarquerez qu'un nombre est pair lorsque le reste de sa division entière par 2 est nul.

### 6.7.7 Conjecture de Syracuse (exercice +++)

La conjecture de Syracuse<fnref n="2" /> est une conjecture mathématique qui reste improuvée à ce jour et qui est définie de la manière suivante.

Soit un entier positif $n$. Si $n$ est pair, alors le diviser par 2. S'il est impair, alors le multiplier par 3 et lui ajouter 1. En répétant cette procédure, la suite de nombres atteint la valeur 1 puis se prolonge indéfiniment par une suite de trois valeurs triviales appelée cycle trivial.

Jusqu'à présent, la conjecture de Syracuse, selon laquelle depuis n'importe quel entier positif la suite de Syracuse atteint 1, n'a pas été mise en défaut.

Par exemple, les premiers éléments de la suite de Syracuse si on prend comme point de départ 10 sont : 10, 5, 16, 8, 4, 2, 1...

Créez un script qui, partant d'un entier positif $n$ (par exemple 10 ou 20), crée une liste des nombres de la suite de Syracuse. Avec différents points de départ (c'est-à-dire avec différentes valeurs de $n$), la conjecture de Syracuse est-elle toujours vérifiée ? Quels sont les nombres qui constituent le cycle trivial ?

#### Conseil

1. Pour cet exercice, vous avez besoin de faire un nombre d'itérations inconnu pour que la suite de Syracuse atteigne le chiffre 1 puis entame son cycle trivial. Vous pourrez tester votre algorithme avec un nombre arbitraire d'itérations, typiquement 20 ou 100, suivant votre nombre $n$ de départ.

2. Un nombre est pair lorsque le reste de sa division entière (opérateur modulo %) par 2 est nul.

<footnote n="2">2. http://fr.wikipedia.org/wiki/Conjecture_de_Syracuse</footnote>
### 6.7.8 Attribution de la structure secondaire des acides aminés d'une protéine (exercice +++)

Dans une protéine, les différents acides aminés sont liés entre eux par une liaison peptidique. Les angles phi et psi sont deux angles mesurés autour de cette liaison peptidique. Leurs valeurs sont utiles pour définir la conformation spatiale (appelée « structure secondaire ») adoptée par les acides aminés.

Par exemple, les angles phi et psi d'une conformation en « hélice alpha » parfaite ont une valeur de -57 degrés et -47 degrés respectivement. Bien sûr, il est très rare que l'on trouve ces valeurs parfaites dans une protéine, et il est habituel de tolérer une déviation de ± 30 degrés autour des valeurs idéales de ces angles.

Vous trouverez ci-dessous une liste de listes contenant les valeurs des angles phi et psi de 15 acides aminés de la protéine 1TFE<fnref n="3" />:

1 [[48.6, 53.4], [-124.9, 156.7], [-66.2, -30.8], \ 2 [-58.8, -43.1], [-73.9, -40.6], [-53.7, -37.5], \ 3 [-80.6, -26.0], [-68.5, 135.0], [-64.9, -23.5], \ 4 [-66.9, -45.5], [-69.6, -41.0], [-62.7, -37.5], \ 5 [-68.2, -38.3], [-61.2, -49.1], [-59.7, -41.1]]

Pour le premier acide aminé, l'angle phi vaut 48.6 et l'angle psi 53.4. Pour le deuxième, l'angle phi vaut -124.9 et l'angle psi 156.7, etc.

En utilisant cette liste, créez un script qui teste, pour chaque acide aminé, s'il est ou non en hélice et affiche les valeurs des angles phi et psi et le message adapté est en hélice ou n'est pas en hélice.

Par exemple, pour les trois premiers acides aminés :

[48.6, 53.4] n'est pas en hélice
[-124.9, 156.7] n'est pas en hélice
[-66.2, -30.8] est en hélice

D'après vous, quelle est la structure secondaire majoritaire de ces 15 acides aminés ?

**Remarque**

Pour en savoir plus sur le monde merveilleux des protéines, n'hésitez pas à consulter la page Wikipedia sur la structure secondaire des protéines<fnref n="4" />.

### 6.7.9 Détermination des nombres premiers inférieurs à 100 (exercice +++)

Voici un extrait de l'article sur les nombres premiers tiré de l'encyclopédie en ligne Wikipédia<fnref n="5" /> :

Un nombre premier est un entier naturel qui admet exactement deux diviseurs distincts entiers et positifs (qui sont alors 1 et lui-même). Cette définition exclut 1, qui n'a qu'un seul diviseur entier positif. Par opposition, un nombre non nul produit de deux nombres entiers différents de 1 est dit composé. Par exemple $6 = 2 \times 3$ est composé, tout comme $21 = 3 \times 7$, mais 11 est premier car 1 et 11 sont les seuls diviseurs de 11. Les nombres 0 et 1 ne sont ni premiers ni composés.

Déterminez tous les nombres premiers inférieurs à 100. Combien y a-t-il de nombres premiers entre 0 et 100 ? Pour vous aider, nous vous proposons plusieurs méthodes.

#### Méthode 1 (peu optimale, mais assez intuitive)

Pour chaque nombre $N$ de 2 à 100, calculez le reste de la division entière (avec l'opérateur modulo $\%$) depuis 1 jusqu'à lui-même. Si $N$ est premier, il aura exactement deux nombres pour lesquels le reste de la division entière est égal à 0 (1 et lui-même). Si $N$ n'est pas premier, il aura plus de deux nombres pour lesquels le reste de la division entière est égal à 0.

<footnote n="3">3. https://www.rcsb.org/structure/1TFE</footnote>

<footnote n="4">4. https://fr.wikipedia.org/wiki/Structure_des_prot%C3%A9ines#Angles_di%C3%A8dres_et_structure_secondaire</footnote>

<footnote n="5">5. http://fr.wikipedia.org/wiki/Nombre_premier</footnote>
## Méthode 2 (quelques petites optimisations qui font gagner du temps)

On reprend la méthode 1 avec deux petites optimisations. On sait que tout entier $N$ supérieur à 1 est divisible par 1 et par lui-même. Ainsi, il est inutile de tester ces deux diviseurs. On propose donc de tester tous les diviseurs de 2 à $N-1$. Si on ne trouve aucun diviseur, alors $N$ est premier. À partir du moment où on trouve un diviseur, il est inutile de continuer à chercher d'autres diviseurs car $N$ ne sera pas premier. On suggère ainsi de stopper la boucle (pensez à break). Vous pourrez aussi utiliser une variable *drapeau* comme *est_premier* qui sera à True si $N$ est premier, sinon à False.

## Méthode 3 (plus optimale et rapide, mais un peu plus compliquée)

Parcourez tous les nombres $N$ de 2 à 100 et vérifiez si ceux-ci sont composés, c'est-à-dire s'ils sont le produit de deux nombres premiers. Pratiquement, cela consiste à vérifier que le reste de la division entière (opérateur modulo %) entre $N$ et chaque nombre premier déterminé jusqu'à maintenant est nul. Le cas échéant, $N$ n'est pas premier.

## 6.7.10 Recherche d'un nombre par dichotomie (exercice +++)

La recherche par dichotomie<fnref n="6" /> est une méthode qui consiste à diviser (en général en parties égales) un problème pour en trouver la solution. À titre d'exemple, voici une discussion entre Pierre et Patrick dans laquelle Pierre essaie de deviner le nombre (compris entre 1 et 100 inclus) auquel Patrick a pensé :

* [Patrick] « C'est bon, j'ai pensé à un nombre entre 1 et 100. »

* [Pierre] « OK, je vais essayer de le deviner. Est-ce que ton nombre est plus petit ou plus grand que 50 ? »

* [Patrick] « Plus grand. »

* [Pierre] « Est-ce que ton nombre est plus petit, plus grand ou égal à 75 ? »

* [Patrick] « Plus grand. »

* [Pierre] « Est-ce que ton nombre est plus petit, plus grand ou égal à 87 ? »

* [Patrick] « Plus petit. »

* [Pierre] « Est-ce que ton nombre est plus petit, plus grand ou égal à 81 ? »

* [Patrick] « Plus petit. »

* [Pierre] « Est-ce que ton nombre est plus petit, plus grand ou égal à 78 ? »

* [Patrick] « Plus grand. »

* [Pierre] « Est-ce que ton nombre est plus petit, plus grand ou égal à 79 ? »

* [Patrick] « Égal. C'est le nombre auquel j'avais pensé. Bravo ! »

Pour arriver rapidement à deviner le nombre, l'astuce consiste à prendre à chaque fois la moitié de l'intervalle dans lequel se trouve le nombre. Voici le détail des différentes étapes :

1. Le nombre se trouve entre 1 et 100, on propose 50 (100 / 2).

2. Le nombre se trouve entre 50 et 100, on propose 75 (50 + (100-50)/2).

3. Le nombre se trouve entre 75 et 100, on propose 87 (75 + (100-75)/2).

4. Le nombre se trouve entre 75 et 87, on propose 81 (75 + (87-75)/2).

5. Le nombre se trouve entre 75 et 81, on propose 78 (75 + (81-75)/2).

6. Le nombre se trouve entre 78 et 81, on propose 79 (78 + (81-78)/2).

Créez un script qui reproduit ce jeu de devinettes. Vous pensez à un nombre entre 1 et 100 et l'ordinateur essaie de le deviner par dichotomie en vous posant des questions.

Votre programme utilisera la fonction *input()* pour interagir avec l'utilisateur. Voici un exemple de son fonctionnement :

1 >>> lettre = input("Entrez une lettre : ")

2 Entrez une lettre : P

3 >>> print(lettre)

4 P

Pour vous guider, voici ce que donnerait le programme avec la conversation précédente :

<footnote n="6">6. https://fr.wikipedia.org/wiki/Dichotomie</footnote>
Pensez à un nombre entre 1 et 100.

Est-ce votre nombre est plus grand, plus petit ou égal à 50 ? [+/-/=] +

Est-ce votre nombre est plus grand, plus petit ou égal à 75 ? [+/-/=] +

Est-ce votre nombre est plus grand, plus petit ou égal à 87 ? [+/-/=] -

Est-ce votre nombre est plus grand, plus petit ou égal à 81 ? [+/-/=] -

Est-ce votre nombre est plus grand, plus petit ou égal à 78 ? [+/-/=] +

Est-ce votre nombre est plus grand, plus petit ou égal à 79 ? [+/-/=] =

J'ai trouvé en 6 questions !

Les caractères [+/-/=] indiquent à l'utilisateur comment il doit interagir avec l'ordinateur, c'est-à-dire entrer soit le caractère + si le nombre choisi est plus grand que le nombre proposé par l'ordinateur, soit le caractère - si le nombre choisi est plus petit que le nombre proposé par l'ordinateur, soit le caractère = si le nombre choisi est celui proposé par l'ordinateur (en appuyant ensuite sur la touche Entrée).
# CHAPITRE 7

## Fichiers

### 7.1 Lecture dans un fichier

Une grande partie de l'information en biologie est stockée sous forme de texte dans des fichiers. Pour traiter cette information, vous devez le plus souvent lire ou écrire dans un ou plusieurs fichiers. Python possède pour cela de nombreux outils qui vous simplifient la vie.

#### 7.1.1 Méthode .readlines()

Avant de passer à un exemple concret, créez un fichier dans l'éditeur de texte de votre choix avec le contenu suivant :

```
girafe
tigre
singe
souris
```

Enregistrez ce fichier dans votre répertoire courant avec le nom animaux.txt. Puis, testez le code suivant dans l'interpréteur Python :

```python
1 >>> filin = open("animaux.txt", "r")
2 >>> filin
3 <_io.TextIOWrapper name='animaux.txt' mode='r' encoding='UTF-8'>
4 >>> filin.readline()
5 ['girafe\n', 'tigre\n', 'singe\n', 'souris\n']
6 >>> filin.close()
7 >>> filin.readline()
8 Traceback (most recent call last):
9  File "<stdin>", line 1, in <module>
10 ValueError: I/O operation on closed file.
```

Il y a plusieurs commentaires à faire sur cet exemple :

* **Ligne 1.** La fonction open() ouvre le fichier animaux.txt. Ce fichier est ouvert en lecture seule, comme l'indique le second argument r (pour read) de open(). Remarquez que le fichier n'est pas encore lu, mais simplement ouvert (un peu comme lorsqu'on ouvre un livre, mais qu'on ne l'a pas encore lu). Le curseur de lecture est prêt à lire le premier caractère du fichier. L'instruction open("animaux.txt", "r") suppose que le fichier animaux.txt est dans le répertoire depuis lequel l'interpréteur Python a été lancé. Si ce n'est pas le cas, il faut préciser le chemin d'accès au fichier. Par exemple, `/home/pierre/animaux.txt` pour Linux ou Mac OS X ou `C:\Users\pierre\animaux.txt` pour Windows.
* **Ligne 2.** Lorsqu'on affiche le contenu de la variable `filin`, on se rend compte que Python la considère comme un objet de type fichier ouvert (ligne 3).

* **Ligne 4.** Nous utilisons à nouveau la syntaxe `objet.méthode()` (présentée dans le chapitre 3 *Affichage*). Ici la méthode `readlines()` agit sur l'objet `filin` en déplaçant le curseur de lecture du début à la fin du fichier, puis elle renvoie une liste contenant toutes les lignes du fichier (dans notre analogie avec un livre, ceci correspondrait à lire toutes les lignes du livre).

* **Ligne 6.** Enfin, on applique la méthode `close()` sur l'objet `filin`, ce qui, vous vous en doutez, ferme le fichier (ceci reviendrait à fermer le livre). Vous remarquerez que la méthode `close()` ne renvoie rien, mais modifie l'état de l'objet `filin` en fichier fermé. Ainsi, si on essaie de lire à nouveau les lignes du fichier, Python renvoie une erreur, car il ne peut pas lire un fichier fermé (lignes 7 à 10).

Voici maintenant un exemple complet de lecture d'un fichier avec Python :

```python
1 >>> filin = open("animaux.txt", "r")
2 >>> lignes = filin.readline()
3 >>> lignes
4 ['girafe\n', 'tigre\n', 'singe\n', 'souris\n']
5 >>> for ligne in lignes:
6     ... print(ligne)
7 ...
8 girafe
9
tigre
10
11
12 singe
13
14 souris
15
16 >>> filin.close()
```

Vous voyez qu'en cinq lignes de code, vous avez lu, parcouru le fichier et affiché son contenu.

Remarque

* Chaque élément de la liste `lignes` est une chaîne de caractères. C'est en effet sous forme de chaînes de caractères que Python lit le contenu d'un fichier.

* Chaque élément de la liste `lignes` se termine par le caractère `\n`. Ce caractère un peu particulier correspond au « saut de ligne<fnref n="1" /> » qui permet de passer d'une ligne à la suivante (en anglais *line feed*). Ceci est codé par un caractère spécial que l'on représente par `\n`. Vous pourrez parfois rencontrer également la notation octale `\012`. Dans la suite de cet ouvrage, nous employerons aussi l'expression « retour à la ligne » que nous trouvons plus intuitive.

* Par défaut, l'instruction `print()` affiche quelque chose puis revient à la ligne. Ce retour à la ligne dû à `print()` se cumule alors avec celui de la fin de ligne (`\n`) de chaque ligne du fichier et donne l'impression qu'une ligne est sautée à chaque fois.

Il existe en Python le mot-clé `with` qui permet d'ouvrir et de fermer un fichier de manière efficace. Si pour une raison ou une autre l'ouverture ou la lecture du fichier conduit à une erreur, l'utilisation de `with` garantit la bonne fermeture du fichier, ce qui n'est pas le cas dans le code précédent. Voici donc le même exemple avec `with` :

<footnote n="1">1. https://fr.wikipedia.org/wiki/Saut_de_ligne</footnote>
```python
1 >>> with open("animaux.txt", 'r') as filin:
2     ...     lignes = filin.readline()
3     ...     for ligne in lignes:
4         ...         print(ligne)
5     ...
6 girafe
7
gigare
8 tigre
9
10 singe
11
12 souris
13
14 >>>
```

### Remarque

* L'instruction `with` introduit un bloc d'instructions qui doit être indenté. C'est à l'intérieur de ce bloc que nous effectuons toutes les opérations sur le fichier.

* Une fois sorti du bloc d'instructions, Python fermera **automatiquement** le fichier. Vous n'avez donc plus besoin d'utiliser la méthode `close()`.

## 7.1.2 Méthode .read()

Il existe d'autres méthodes que `readlines()` pour lire (et manipuler) un fichier. Par exemple, la méthode `read()` lit tout le contenu d'un fichier et renvoie une chaîne de caractères unique :

```python
1 >>> with open("animaux.txt", "r") as filin:
2     ...     filin.read()
3     ...
4 'girafe\ntigre\nsinge\nsouris\n'
5 >>>
```

## 7.1.3 Méthode .readline()

La méthode `readline()` (sans s à la fin) lit une ligne d'un fichier et la renvoie sous forme de chaîne de caractères. À chaque nouvel appel de `readline()`, la ligne suivante est renvoyée. Associée à la boucle `while`, cette méthode permet de lire un fichier ligne par ligne :

```python
1 >>> with open("animaux.txt", "r") as filin:
2     ...     ligne = filin.readline()
3     ...     while ligne != "":
4         ...         print(ligne)
5     ...
6 ...
7 girafe
8
gigare
9 tigre
10 singe
11 souris
12
13 >>>
```

## 7.1.4 Itérations directes sur le fichier

Python essaie de vous faciliter la vie au maximum. Voici un moyen à la fois simple et élégant de parcourir un fichier :
```python
1 >>> with open("animaux.txt", "r") as filin:
2     ... for ligne in filin:
3     ...     print(ligne)
4
5 girafe
6
7 tigre
8
9 singe
10
11 souris
12
13 >>>
```

L'objet `filin` est « itérable », ainsi la boucle `for` va demander à Python d'aller lire le fichier ligne par ligne.

### Conseil

Privilégiez cette méthode par la suite.

### Remarque

Les méthodes abordées précédemment permettent d'accéder au contenu d'un fichier, soit ligne par ligne (méthode `.readline()`), soit globalement en une seule chaîne de caractères (méthode `.read()`), soit globalement avec les lignes différenciées sous forme d'une liste de chaînes de caractères (méthode `.readlines()`). Il est également possible en Python de se rendre à un endroit particulier d'un fichier avec la méthode `.seek()` mais qui sort du cadre de cet ouvrage.

## 7.2 Écriture dans un fichier

Écrire dans un fichier est aussi simple que de le lire. Voyez l'exemple suivant :

```python
1 >>> animaux2 = ["poisson", "abeille", "chat"]
2 >>> with open("animaux2.txt", "w") as filout:
3     ... for animal in animaux2:
4     ...     filout.write(animal)
5
6 7
7 7
8 4
```

Quelques commentaires sur cet exemple :

* **Ligne 1.** Création d'une liste de chaînes de caractères `animaux2`.

* **Ligne 2.** Ouverture du fichier `animaux2.txt` en mode écriture, avec le caractère `w` pour `write`. L'instruction `with` crée un bloc d'instructions qui doit être indenté.

* **Ligne 3.** Parcours de la liste `animaux2` avec une boucle `for`.

* **Ligne 4.** À chaque itération de la boucle, nous avons écrit chaque élément de la liste dans le fichier. La méthode `.write()` s'applique sur l'objet `filout`. Notez qu'à chaque utilisation de la méthode `.write()`, celle-ci nous affiche le nombre d'octets (équivalent au nombre de caractères) écrits dans le fichier (lignes 6 à 8). Ceci est valable uniquement dans l'interpréteur. Si vous créez un programme avec les mêmes lignes de code, ces valeurs ne s'afficheront pas à l'écran.

Si nous ouvrons le fichier `animaux2.txt` avec un éditeur de texte, voici ce que nous obtenons :

```
poissonabeillechat
```

Ce n'est pas exactement le résultat attendu car implicitement nous voulions le nom de chaque animal sur une ligne. Nous avons oublié d'ajouter le caractère fin de ligne après chaque nom d'animal.

Pour ce faire, nous pouvons utiliser l'écriture formatée :
1 >>> animaux2 = ["poisson", "abeille", "chat"]

2 >>> with open("animaux2.txt", "w") as filout:

3 ... for animal in animaux2:

4 ... filout.write(f"{animal}
")

5 ...

6 8

7 8

8 5

* **Ligne 4.** L'écriture formatée, vue au chapitre 3 *Affichage*, permet d'ajouter un retour à la ligne (`\n`) après le nom de chaque animal.

* **Lignes 6 à 8.** Le nombre d'octets écrits dans le fichier est augmenté de 1 par rapport à l'exemple précédent, car le caractère retour à la ligne compte pour un seul octet.

Le contenu du fichier animaux2.txt est alors :

poisson

abeille

chat

Vous voyez qu'il est relativement simple en Python de lire ou d'écrire dans un fichier.

## 7.3 Ouvrir deux fichiers avec l'instruction with

On peut avec l'instruction with ouvrir deux fichiers (ou plus) en même temps. Voyez l'exemple suivant :

1 with open("animaux.txt", "r") as fichier1, open("animaux2.txt", "w") as fichier2:

2 for ligne in fichier1:

3 fichier2.write("* " + ligne)

Si le fichier animaux.txt contient le texte suivant :

souris

girafe

lion

singe

alors le contenu de animaux2.txt sera :

* souris

* girafe

* lion

* singe

Dans cet exemple, with permet une notation très compacte en s'affranchissant de deux méthodes .close() .

Si vous souhaitez aller plus loin, sachez que l'instruction with est plus générale et peut être utilisée dans d'autres contextes<fnref n="2" />.

## 7.4 Note sur les retours à la ligne sous Unix et sous Windows

### Conseil

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

On a vu plus haut que le caractère spécial `\n` correspondait à un retour à la ligne. C'est le standard sous Unix (Mac OS X et Linux).

Toutefois, Windows utilise deux caractères spéciaux pour le retour à la ligne : `\r` correspondant à un retour chariot (hérité des machines à écrire) et `\n` comme sous Unix.

Si vous avez commencé à programmer en Python 2, vous aurez peut-être remarqué que, selon les versions, la lecture de fichier supprimait parfois les `\r` et d'autres fois les laissait. Heureusement, la fonction `open()` dans Python 3³ gère

<footnote n="2">2. https://docs.python.org/fr/3/reference/compound_stmts.html#the-with-statement</footnote>

<footnote n="3">3. https://docs.python.org/fr/3/library/functions.html#open</footnote>
tout ça automatiquement et renvoie uniquement des sauts de ligne sous forme d'un seul \n (même si le fichier a été conçu sous Windows et qu'il contient initialement des \r).

## 7.5 Importance des conversions de types avec les fichiers

Vous avez sans doute remarqué que les méthodes qui lisent un fichier (par exemple `readlines()`) vous renvoient systématiquement des chaînes de caractères. De même, pour écrire dans un fichier, il faut fournir une chaîne de caractères à la méthode `write()`.

Pour tenir compte de ces contraintes, il faudra utiliser les fonctions de conversion de types vues au chapitre 2 *Variables : int()*, `float()` et `str()`. Ces fonctions de conversion sont essentielles lorsqu'on lit ou écrit des nombres dans un fichier.

En effet, les nombres dans un fichier sont considérés comme du texte, donc comme des chaînes de caractères, par la méthode `readlines()`. Par conséquent, il faut les convertir (en entier ou en `float`) si on veut effectuer des opérations numériques avec.

## 7.6 Du respect des formats de données et de fichiers

Maintenant que vous savez lire et écrire des fichiers en Python, vous êtes capables de manipuler beaucoup d'information en biologie. Prenez garde cependant aux formats de fichiers, c'est-à-dire à la manière dont est stockée l'information biologique dans des fichiers. Nous vous renvoyons pour cela à l'annexe A *Quelques formats de données en biologie*.

## 7.7 Exercices

### Conseil

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

### 7.7.1 Moyenne des notes

Le fichier `notes.txt`<fnref n="4" /> contient les notes obtenues par des étudiants pour le cours de Python. Chaque ligne du fichier ne contient qu'une note.

Téléchargez le fichier `notes.txt` et enregistrez-le dans votre répertoire de travail. N'hésitez pas à l'ouvrir avec un éditeur de texte pour voir à quoi il ressemble.

Créez un script Python qui lit chaque ligne de ce fichier, extrait les notes sous forme de `float` et les stocke dans une liste.

Terminez le script en calculant et affichant la moyenne des notes avec deux décimales.

### 7.7.2 Admis ou recalé

Téléchargez le fichier `notes.txt` de l'exercice précédent et enregistrez-le dans votre répertoire de travail. N'hésitez pas l'ouvrir avec un éditeur de texte pour voir à quoi il ressemble.

Créez un script Python qui lit chaque ligne de ce fichier, extrait les notes sous forme de `float` et les stocke dans une liste.

Le script réécrira ensuite les notes dans le fichier `notes2.txt` avec une note par ligne suivie de « recalé » si la note est inférieure à 10 et « admis » si la note est supérieure ou égale à 10. Toutes les notes seront écrites avec une décimale. À titre d'exemple, voici les trois premières lignes attendues pour le fichier `notes2.txt`:

```
13.5 admis
17.0 admis
9.5 recalé
```

### 7.7.3 Spirale (exercice +++)

Créez un script `spirale.py` qui calcule les coordonnées cartésiennes d'une spirale à deux dimensions.

<footnote n="4">4. https://python.sdv.u-paris.fr/data-files/notes.txt</footnote>
Les coordonnées cartésiennes $x_A$ et $y_A$ d'un point A sur un cercle de rayon $r$ s'expriment en fonction de l'angle $\theta$ représenté sur la figure 7.1 comme :

$$x_A = \cos(\theta) \times r$$

$$y_A = \sin(\theta) \times r$$

<figure><img src="image_11.png" /><figcaption>FIGURE 7.1 – Point A de coordonnées $(x_A, y_A)$ sur le cercle de rayon $r$.</figcaption></figure>

Pour calculer les coordonnées cartésiennes qui décrivent la spirale, vous allez faire varier deux variables en même temps :

* l'angle $\theta$, qui va prendre des valeurs de 0 à $4\pi$ radians par pas de 0,1, ce qui correspond à deux tours complets;

* le rayon du cercle $r$, qui va prendre comme valeur initiale 0,5 puis que vous allez incrémenter (c'est-à-dire augmenter) par pas de 0,1.

Les fonctions trigonométriques sinus et cosinus sont disponibles dans le module `math` que vous découvrirez plus en détails dans le chapitre 9 *Modules*. Pour les utiliser, vous ajouterez au début de votre script l'instruction :

import math

La fonction sinus sera `math.sin()` et la fonction cosinus `math.cos()`. Ces deux fonctions prennent comme argument une valeur d'angle en radian. La constante mathématique $\pi$ sera également accessible grâce à ce module via `math.pi`. Par exemple :

```
1 >>> math.sin(0)
2 0.0
3 >>> math.sin(math.pi/2)
4 1.0
5 >>> math.cos(math.pi)
6 -1.0
```

Sauvegardez ensuite les coordonnées cartésiennes dans le fichier `spirale.dat` en respectant le format suivant :

* un couple de coordonnées $(x_A$ et $y_A$) par ligne;

* au moins un espace entre les deux coordonnées $x_A$ et $y_A$;

* les coordonnées affichées sur 10 caractères avec 5 chiffres après la virgule.

Les premières lignes de `spirale.dat` devrait ressembler à :
```
0.50000  0.00000
0.59700  0.05990
0.68605  0.13907
0.76427  0.23642
0.82895  0.35048
0.87758  0.47943
[...]    [...]
```

Une fois que vous avez généré le fichier `spirale.dat`, visualisez votre spirale avec le code suivant (que vous pouvez recopier dans un autre script ou à la suite de votre script `spirale.py`):

```python
1 import matplotlib.pyplot as plt
2
3 x = []
4 y = []
5 with open("spirale.dat", "r") as f_in:
6     for line in f_in:
7         coordinates = line.split()
8         x.append(floatibleline[0]))
9         y.append(floatableline[1]))
10
11 fig, ax = plt.subplots(figsize=(8,8))
12 mini = min(x+y) - 2
13 maxi = max(x+y) + 2
14 ax.set_xlim(mini, maxi)
15 ax.set_ylim(mini, maxi)
16 ax.plot(x, y)
17 fig.savefig("spirale.png")
```

Visualisez l'image `spirale.png` ainsi créée.

### Remarque

Le module `VESSEL` est utilisé ici pour la visualisation de la spirale. Son utilisation est détaillée dans le chapitre 21 *Module VESSEL*.

Essayez de jouer sur les paramètres $\theta$ et $r$, et leur pas d'incrémentation, pour construire de nouvelles spirales.
# Dictionnaires et tuples

Dans ce chapitre, nous allons voir deux nouveaux types d'objet qui s'avèrent extrêmement utiles : les dictionnaires et les tuples. Comme les listes vues dans le chapitre 4, les dictionnaires et tuples contiennent une collection d'autres objets. Toutefois, nous verrons que ces trois types sont régis par des règles différentes pour accéder à leur contenu, ainsi que dans leur fonctionnement.

## 8.1 Dictionnaires

### 8.1.1 Définition et fonctionnement

#### Définition

Un **dictionnaire** contient une collection d'objets Python auxquels on accède à l'aide d'une **clé** de correspondance plutôt qu'un indice. Ainsi, il ne s'agit pas d'objets séquentiels comme les listes, mais plutôt d'objets dits de correspondance (*mapping objects* en anglais) ou tableaux associatifs.

Ceci étant défini, comment fonctionnent-ils exactement ? Regardons un exemple :

```python
1 >>> animal1 = {}
2 >>> animal1["nom"] = "girafe"
3 >>> animal1["taille"] = 5.0
4 >>> animal1["poids"] = 1100
5 >>> animal1
6 {'nom': 'girafe', 'taille': 5.0, 'poids': 1100}
```

* **Ligne 1.** On définit un dictionnaire vide avec les accolades `{}` (tout comme on peut le faire pour les listes avec `[]`).

* **Lignes 2 à 4.** On remplit le dictionnaire avec plusieurs clés (`"nom"`, `"taille"`, `"poids"`) auxquelles on affecte des valeurs (`"girafe"`, `5.0`, `1100`).

* **Ligne 5.** On affiche le contenu du dictionnaire. Les accolades nous montrent qu'il s'agit bien d'un dictionnaire, et pour chaque élément séparé par une virgule on a une association du type `clé: valeur`. Ici, les clés sont des chaînes de caractères (ce qui sera souvent le cas), et les valeurs peuvent être n'importe quel objet Python.

Une fois le dictionnaire créé, on récupère la valeur associée à une clé donnée avec une syntaxe du type `dictionnaire ["clé"]`. Par exemple :
1 >>> animal2 ={ 'nom':'singe','poids':70,'taille':1.75}

2 >>> for key in animal2:

3 ... print(key, animal2[key])

4 ...

5 poids 70

6 nom singe

7 taille 1.75

Par défaut, l'itération sur un dictionnaire se fait sur les clés. Dans cet exemple, la variable d'itération key prend successivement la valeur de chaque clé, animal2[key] donne la valeur correspondant à chaque clé.

8.1.4 Méthodes .keys() et .values()

Les méthodes keys() et values() renvoient, comme vous vous en doutez, les clés et les valeurs d'un dictionnaire :

1 >>> animal2.keys()

2 dict_keys(['poids', 'nom', 'taille'])

3 >>> animal2.values()

4 dict_values([70, 'singe', 1.75])

Les mentions dict_keys et dict_values indiquent que nous avons à faire à des objets un peu particuliers. Ils ne sont pas indexables (on ne peut pas retrouver un élément par indice, par exemple dico.keys()[0] renverra une erreur). Si besoin, nous pouvons les transformer en liste avec la fonction list() :

1 >>> animal2.values()

2 dict_values(['singe', 70, 1.75])

3 >>> list(animal2.values())

4 ['singe', 70, 1.75]

Toutefois, on peut itérer dessus dans une boucle (on dit qu'ils sont itérables) :

1 >>> for cle in animal2.keys():

2 ... print(cle)

3 ...

4 nom

5 poids

6 taille

8.1.5 Méthode .items()

La méthode .items() renvoie un nouvel objet dict_items :

1 >>> dico ={0: "t",1: "o",2: "t",3: "o"}

2 >>> dico.items()

3 dict_items([ (0, 't'), (1, 'o'), (2, 't'), (3, 'o')])

On ne peut pas retrouver un élément par son indice dans un objet dict_items, toutefois on peut itérer dessus :

1 >>> dico.items()[2]

2 Traceback (most recent call last):

3 File "<stdin>", line 1, in <module>

4 TypeError: 'dict_items' object is not subscriptable

5 >>> for key, val in dico.items():

6 ... print(key, val)

7 ...

8 0 t

9 1 o

10 2 t

11 3 o

Notez la syntaxe particulière qui ressemble à la fonction enumerate() vue au chapitre 5 *Boucles et comparaisons*.
On itère à la fois sur key et sur val. Nous aurons l'explication de ce mécanisme dans la rubrique sur les tuples ci-après.

8.1.6 Existence d'une clé ou d'une valeur

Pour vérifier si une clé existe dans un dictionnaire, on peut utiliser le test d'appartenance avec l'opérateur in qui
renvoie un booléen :
1 >>> animal2 ={ 'nom':'singe','poids':70,'taille':1.75}
2 >>> "poids" in animal2
3 True
4 >>> if "poids" in animal2:
5 ...     print("La clé 'poids' existe pour animal2")
6 ...
7 La clé 'poids' existe pour animal2
8 >>> "age" in animal2
9 False
10 >>> if "age" in animal2:
11 ...     print("La clé 'age' existe pour animal2")
12 ...

Dans le second test (lignes 10 à 12), le message n'est pas affiché car la clé age n'est pas présente dans le dictionnaire animal2.

Si on souhaite tester si une valeur existe dans un dictionnaire, on peut utiliser l'opérateur in avec l'objet renvoyé par la méthode .values() :

1 >>> animal2 ={ 'nom':'singe','poids':70,'taille':1.75}
2 >>> animal2.values()
3 dict_values(['singe', 70, 1.75])
4 >>> "singe" in animal2.values()
5 True

8.1.7 Méthode .get()

Par défaut, si on demande la valeur associée à une clé qui n'existe pas, Python renvoie une erreur :

1 >>> animal2 ={ 'nom':'singe','poids':70,'taille':1.75}
2 >>> animal2["age"]
3 Traceback (most recent call last):
4     File "<stdin>", line 1, in <module>
5 KeyError: 'age'

La méthode .get() s'affranchit de ce problème. Elle extrait la valeur associée à une clé mais ne renvoie pas d'erreur
si la clé n'existe pas :

1 >>> animal2.get("nom")
2 'singe'
3 >>> animal2.get("age")
4 >>>

Ici, la valeur associée à la clé nom est singe, mais la clé age n'existe pas. On peut également indiquer à .get() une valeur par défaut si la clé n'existe pas :

1 >>> animal2.get("age", 42)
2 42

8.1.8 Liste de dictionnaires

En créant une liste de dictionnaires qui possèdent les mêmes clés, on obtient une structure qui ressemble à une base
de données :

1 >>> animaux = [animal1, animal2]
2 >>> animaux
3 [('nom': 'girafe', 'poids': 1100, 'taille': 5.0}, {'nom': 'singe',
4 'poids': [70, 'taille': 1.75]})
5 >>>
6 >>> for ani in animaux:
7 ...     print(ani["nom"])
8 ...
9 girafe
10 singe

Vous constatez ainsi que les dictionnaires permettent de gérer des structures complexes de manière plus explicite que
les listes.
Toutefois, afin d'éviter les confusions, nous vous conseillons d'utiliser systématiquement les parenthèses lorsque vous débutez.

Les opérateurs `+` et `*` fonctionnent comme pour les listes (concaténation et duplication) :

```
1 >>> (1, 2) + (3, 4)
2 (1, 2, 3, 4)
3 >>> (1, 2) * 4
4 (1, 2, 1, 2, 1, 2, 1, 2)
```

Enfin, on peut utiliser la fonction `tuple(sequence)` qui fonctionne exactement comme la fonction `list()`, c'est-à-dire qu'elle prend en argument un objet et renvoie le tuple correspondant (opération de *casting*) :

```
1 >>> tuple([1,2,3])
2 (1, 2, 3)
3 >>> tuple("ATGCCGCGAT")
4 ('A', 'T', 'G', 'C', 'C', 'G', 'C', 'G', 'A', 'T')
5 >>> tuple(range(5))
6 (0, 1, 2, 3, 4)
```

### Remarque

Comme la fonction `list()`, la fonction `tuple()` prend en argument un objet contenant d'autres objets. Elle ne fonctionne pas avec les entiers, `floats` ou `booléens`. Par exemple, `tuple(2)` renvoie une erreur. On en verra plus sur ces questions dans le chapitre 14 *Conteneurs*.

## 8.2.2 Affectation multiple

Les tuples sont souvent utilisés pour l'**affectation multiple**, c'est-à-dire, affecter des valeurs à plusieurs variables en même temps :

```
1 >>> x, y, z = 1, 2, 3
2 >>> x
3 1
4 >>> y
5 2
6 >>> z
7 3
```

Attention, le nombre de variables et de valeurs doit être cohérents à gauche et à droite de l'opérateur `=` :

```
1 >>> x, y = 1, 2, 3
2 Traceback (most recent call last):
3   File "<stdin>", line 1, in <module>
4 ValueError: too many values to unpack (expected 2)
```

Il est aussi possible de faire des affectations multiples avec des listes, par exemple :

`[x, y, z] = [1, 2, 3]`.

Toutefois, cette syntaxe est alourdie par la présence des crochets. On préfèrera donc la syntaxe avec les tuples sans parenthèses.

### Remarque

Nous avons appelé l'opération `x, y, z = 1, 2, 3` affectation multiple pour signifier que l'on affectait des valeurs à plusieurs variables en même temps.

Vous pourrez rencontrer aussi l'expression *tuple unpacking* que l'on pourrait traduire par « désempaquetage de tuple ». De même, il existe le *list unpacking*.

Ce terme *tuple unpacking* provient du fait que l'on peut décomposer un tuple initial de *n* éléments en autant de variables différentes en une seule instruction.

Par exemple, si on crée un tuple de trois éléments :
1 >>> liste =[(5,6,7),(6,7,8),(7,8,9)]

2 >>> for x,y,z in liste:

3 ... print(x, y, z)

4 ...

5 5 6 7

6 6 7 8

7 7 8 9

On pourrait concevoir la même chose sur quatre ou cinq éléments, voire plus. La seule contrainte est d'avoir une correspondance systématique entre le nombre de variables d'itération (par exemple trois variables dans l'exemple ci-dessus avec x, y, z) et la longueur de chaque sous-tuple de la liste sur laquelle on itère (dans l'exemple ci-dessus, chaque sous-tuple a trois éléments).

### 8.2.4 Fonction divmod()

Dans le chapitre 2 *Variables*, on a vu les opérateurs `//` et `%` qui renvoient respectivement le quotient et le reste d'une division entière. La fonction `divmod()` prend en argument deux valeurs, le numérateur et le dénominateur d'une division, et renvoie le quotient et le reste de la division entière correspondante :

1 >>> 3 / 4

2 0.75

3 >>> 3 // 4

4 0

5 >>> 3 % 4

6 3

7 >>> divmod(3, 4)

8 (0, 3)

En utilisant l'affectation multiple, on peut ainsi récupérer à la volée le quotient et le reste en une seule ligne :

1 >>> quotient, reste = divmod(3, 4)

2 >>> quotient

3 0

4 >>> reste

5 3

Cette fonction est très pratique, notamment quand on souhaite convertir des secondes en minutes et secondes résiduelles. Par exemple, si on veut convertir 754 secondes en minutes :

1 >>> 754 / 60

2 12.5666666666666666

3 >>> divmod(754, 60)

4 (12, 34)

La division normale nous donne un *float* en minutes qui n'est pas très pratique, il faut encore convertir `0.5666666666666666` minute en secondes et gérer les problèmes d'arrondi. La fonction `divmod()` renvoie le résultat directement : 12 min et 34 s. On pourrait raisonner de manière similaire pour convertir des minutes en heures, des heures en jours, etc.

### 8.2.5 Remarque finale

Les listes, dictionnaires, tuples et chaînes de caractères sont tous des objets contenant une collection d'autres objets. En Python, on peut construire des listes qui contiennent des dictionnaires, des tuples ou d'autres listes, mais aussi des dictionnaires contenant des tuples, des listes, etc. Les combinaisons sont infinies !

## 8.3 Exercices

### Conseil

Pour le premier exercice, utilisez l'interpréteur Python. Pour les suivants, créez des scripts puis exécutez-les dans un shell.
### 8.3.1 Prédire la sortie

Soit les deux lignes de code suivantes :

```python
1 dico = {"nom": "Joe", "age": 24, "taille": 181}
```

```python
2 var = "nom"
```

Prédisez le comportement de chaque instruction ci-dessous, sans les recopier dans un script ni dans l'interpréteur Python :

* `print(dico["age"])`

* `print(dico[var])`

* `print(dico[24])`

* `print(dico["var"])`

* `print(dico["taille"])`

Lorsqu'une instruction produit une erreur, identifiez pourquoi. Vérifiez ensuite vos prédictions en recopiant les instructions dans l'interpréteur Python.

### 8.3.2 Moyennes des notes

Soit le dictionnaire suivant donnant les notes d'un étudiant :

```python
1 dico_notes = {
2   "math": 14, "programmation": 12,
3   "anglais": 16, "biologie": 10,
4   "sport": 19
5 }
```

Calculez la moyenne de ses notes de deux manières différentes. Calculez à nouveau la moyenne sans la note de biologie.

### 8.3.3 Composition en acides aminés

En utilisant un dictionnaire, déterminez le nombre d'occurrences de chaque acide aminé dans la séquence AGWPSGGASAGLAILWGASAIM. Le dictionnaire ne doit contenir que les acides aminés présents dans la séquence.

Vous ne pouvez pas utiliser autant d'instructions if que d'acides aminés différents. Pensez au test d'appartenance.

### 8.3.4 Convertisseur de secondes

Un athlète court un marathon, malheureusement sa montre ne mesure son temps qu'en secondes. Celle-ci affiche 11 905. Aidez-le à convertir son temps en heures, minutes et secondes avec la fonction `divmod()`.

### 8.3.5 Convertisseur de jours

L'âge de Camille et Céline en jours est respectivement de 8 331 jours et 8 660 jours. Quel est leur âge en années, mois et jours, en supposant qu'une année compte 365 jours et qu'un mois compte 30 jours ? La fonction `divmod()` vous aidera à nouveau.

### 8.3.6 Propriétés des acides aminés

Les acides aminés peuvent être séparés en quatre grandes catégories : apolaires (a), polaires (p), chargés positivement (+) et chargés négativement (-). Le dictionnaire suivant implémente cette classification :

```python
1 aa2prop = {'A': 'a', 'V': 'a', 'L': 'a', 'G': 'a', 'I': 'a', 'M': 'a',
2           'W': 'a', 'F': 'a', 'P': 'a',
3           'S': 'p', 'C': 'p', 'N': 'p', 'Q': 'p', 'T': 'p', 'Y': 'p',
4           'D': '+', 'E': '+',
5           'K': '-', 'R':，则 'H': }
```

On souhaite convertir la séquence en acide aminé du domaine transmembranaire d'une intégrine humaine SNADVYYEKQMLYLYVLSGIG en une série de signes indiquant la nature des acides aminés (a, p, + et -). Affichez tout d'abord la séquence sur une ligne, puis la nature des acides aminés sur une seconde ligne.

La séquence contient une hélice transmembranaire, donc une succession de résidus apolaires, essayez de la retrouver visuellement.
Pour cet exercice, nous vous conseillons d'itérer sur la chaîne de caractères contenant la séquence. Nous reverrons
cela dans le chapitre 11 *Plus sur les chaînes de caractères*.

8.3.7 Boucle sur plusieurs éléments simultanément

À partir de la liste de tuples suivante :

[("chien", 3), ("chat", 4), ("souris", 16)]

affichez chaque animal et son nombre en utilisant qu'une seule boucle for. Attention, pour cet exercice, il est interdit
d'utiliser l'individage des listes.
# CHAPITRE 9

## Modules

### 9.1 Définition

Les modules sont des programmes Python qui contiennent des fonctions que l'on est amené à souvent réutiliser (on les appelle aussi bibliothèques, ou libraries en anglais). Ce sont des « boîtes à outils » qui vous seront très utiles.

Les développeurs de Python ont mis au point de nombreux modules qui effectuent différentes tâches. Pour cette raison, prenez toujours le réflexe de vérifier si une partie du code que vous souhaitez écrire n'existe pas déjà sous forme de module.

La plupart de ces modules sont déjà installés dans les versions standards de Python. Vous pouvez accéder à une documentation exhaustive<fnref n="1" /> sur le site de Python. N'hésitez pas à explorer un peu ce site, la quantité de modules disponibles est impressionnante (plus de 300 modules).

### 9.2 Importation de modules

Dans les chapitres précédents, nous avons rencontré la notion de module plusieurs fois, notamment lorsque nous avons voulu tirer un nombre aléatoire :

1 >>> import random
2 >>> randomonde(0, 10)
3 4

Regardons de plus près cet exemple :

* **Ligne 1.** L'instruction `import` donne accès à toutes les fonctions du module `random`<fnref n="2" />.

* **Ligne 2.** Nous utilisons la fonction `randint(0, 10)` du module `random`. Cette fonction renvoie un nombre entier tiré aléatoirement entre 0 inclus et 10 inclus.

Nous avons également croisé le module `math` lors de l'exercice sur la spirale (voir le chapitre 7 *Fichiers*). Ce module nous a donné accès aux fonctions trigonométriques sinus et cosinus, et à la constante $\pi$ :

1 >>> import math
2 >>> math.cos(math.pi / 2)
3 6.123233995736766e-17
4 >>> math.sin(math.pi / 2)
5 1.0

<footnote n="1">1. https://docs.python.org/fr/3/py-modindex.html</footnote>

<footnote n="2">2. https://docs.python.org/fr/3/library/random.html#module-random</footnote>
En résumé, l'utilisation de la syntaxe `import module` permet d'importer tout une série de fonctions organisées par « thèmes ». Par exemple, les fonctions gérant les nombres aléatoires avec `random` et les fonctions mathématiques avec `math`. Python possède de nombreux autres modules internes (c'est-à-dire présent de base lorsqu'on installe Python).

### Remarque

Dans le chapitre 3 *Affichage*, nous avons introduit la syntaxe `truc.bidule()` avec `truc` étant un objet et `.bidule()` une méthode. Nous vous avions expliqué qu'une *méthode* était une fonction un peu particulière :

* elle était liée à un objet par un point;

* en général, elle agissait sur ou utilisait l'objet auquel elle était liée.

Par exemple, la méthode `.append()` vue dans le chapitre 4 *Listes*. Dans l'instruction `listel.append(3)`, la méthode `.append()` ajoute l'entier 3 à l'objet `listel` auquel elle est liée.

Avec les modules, nous rencontrons une syntaxe similaire. Par exemple, dans l'instruction `math.cos()`, on pourrait penser que `.cos()` est aussi une méthode. En fait la documentation officielle de Python 3 précise bien que dans ce cas `.cos()` est une fonction. Dans cet ouvrage, nous utiliserons ainsi le mot **fonction** lorsqu'on évoquera des fonctions issues de modules.

Si cela vous parait encore ardu, ne vous inquiétez pas : c'est à force de pratiquer et de lire que vous vous approprierez le vocabulaire. La syntaxe `module.fonction()` est là pour rappeler de quel module provient la fonction en un coup d'œil.

Il existe un autre moyen d'importer une ou plusieurs fonctions d'un module :

1 >>> from random import random
2 >>> random(0,10)
3 7

À l'aide du mot-clé `from`, on peut importer une fonction spécifique d'un module donné. Remarquez bien qu'il est inutile de répéter le nom du module dans ce cas : seul le nom de la fonction en question est requis.

On peut également importer toutes les fonctions d'un module :

1 >>> from random import *
2 >>> random(0,50)
3 46
4 >>> uniform(0,2.5)
5 0.64943174760727951

L'instruction `from random import *` importe toutes les fonctions du module `random`. On peut utiliser toutes ses fonctions directement, comme par exemple `randint()` et `uniform()` qui renvoient des nombres aléatoires entiers et `floats`.

Dans la pratique, plutôt que de charger toutes les fonctions d'un module en une seule fois :

1 from random import *

Nous vous conseillons de charger le module seul de la manière suivante :

1 import random

puis d'appeler explicitement les fonctions voulues, par exemple :

1 >>> import random
2 >>> random.random(1, 10)
3 4
4 >>> random/uniform(1, 3)
5 1.8645753676306085

Il est également possible de définir un alias (un nom plus court) pour un module :

<footnote n="3">3. https://docs.python.org/fr/3/tutorial/modules.html</footnote>
1 >>> import random as rand

2 >>> rand.random(1, 10)

3 6

4 >>> rand/uniform(1, 3)

5 2.643472616544236

Dans cet exemple, les fonctions du module *random* sont accessibles via l'alias *rand*.

Enfin, pour vider de la mémoire un module déjà chargé, on peut utiliser l'instruction `del` :

1 >>> import random

2 >>> random.random(0,10)

3 2

4 >>> random/uniform(1, 3)

5 2.825594756352219

6 >>> del random

7 >>> random.random(0,10)

8 Traceback (most recent call last):

9 File "<stdin>", line 1, in ?

10 NameError: name 'random' is not defined. Did you forget to import 'random'?

On constate alors qu'un rappel (ligne 7) d'une fonction du module *random*, après l'avoir vidé de la mémoire (ligne 6), retourne un message d'erreur (lignes 8-10). Dans le cas présent, le message d'erreur est explicite et demande à l'utilisateur s'il n'a pas oublié d'importer le module *random*.

## 9.3 Obtenir de l'aide sur les modules importés

Pour obtenir de l'aide sur un module, rien de plus simple : il suffit d'utiliser la commande `help()` :

1 >>> import random

2 >>> help(random)

3 [...]

Ce qui renvoie :

Help on module random:

NAME

random - Random variable generators.

MODULE REFERENCE

https://docs.python.org/3.7/library/random

The following documentation is automatically generated from the Python
source files. It may be incomplete, incorrect or include features that
are considered implementation detail and may vary between Python
implementations. When in doubt, consult the module reference at the
location listed above.

DESCRIPTION

integers

---------

uniform within range

sequences

---------

pick random element

pick random sample

Remarque

* Pour vous déplacer dans l'aide, utilisez les flèches du haut et du bas pour le parcourir ligne par ligne, ou les touches Page-up et Page-down pour faire défiler l'aide page par page.

* Pour quitter l'aide, appuyez sur la touche Q.

* Pour chercher du texte, tapez le caractère / puis le texte que vous cherchez, puis la touche Entrée. Par exemple, pour chercher l'aide sur la fonction `randint()`, tapez `/randint` puis Entrée.
* Vous pouvez également obtenir de l'aide sur une fonction particulière d'un module avec :
`help(randomrandint)`

La commande `help()` est en fait une commande plus générale, permettant d'avoir de l'aide sur n'importe quel objet chargé en mémoire :

```python
1 >>> t = [1, 2, 3]
2 >>> help(t)
3 Help on list object:
4
5 class list(object)
6 | list() -> new list
7 | list(sequence) -> new list initialized from sequence's items
8 |
9 | Methods defined here:
10 |
11 | __add__()
12 | x.__add__(y) <=> x+y
13 |
14 |
15 ...
```

Enfin, pour connaître d'un seul coup d'œil toutes les méthodes ou variables associées à un objet, utilisez la fonction `dir()` :

```python
1 >>> import random
2 >>> dir(random)
3 ['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST',
4 'SystemRandom', 'TWOPI', 'WichmannHill', '_BuiltinMethodType', '_MethodT
5 type', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__ac
6 os', '__ceil', '__cos', '__e', '__exp', '__hexlify', '__inst', '__log', '__pi',
7 '__random', '__sin', '__sqrt', '__test', '__test_generator', '__urandom', '__wa
8 rn', 'betavariate', 'choice', 'expovariate', 'gammavariate', 'gauss', 'g
9 etrandbits', 'getstate', 'jumpahead', 'lognormvariate', 'normalvariate',
10 'paretovariate', 'randint', 'random', 'randrange', 'sample', 'seed', 's
11 etstate', 'shuffle', 'uniform', 'vonmisesvariate', 'weibullvariate']
12 >>>
```

## 9.4 Quelques modules courants

Il existe une série de modules que vous serez probablement amenés à utiliser si vous programmez en Python. En voici une liste non exhaustive (pour la liste complète, reportez-vous à la page des modules<fnref n="4" /> sur le site de Python) :

* `math`<fnref n="5" /> : fonctions et constantes mathématiques de base (`sin`, `cos`, `exp`, `pi`...).

* `sys`<fnref n="6" /> : interaction avec l'interpréteur Python, notamment pour le passage d'arguments (voir plus bas).

* `pathlib`<fnref n="7" /> : gestion des fichiers et des répertoires (voir plus bas).

* `random`<fnref n="8" /> : génération de nombres aléatoires.

* `time`<fnref n="9" /> : accès à l'heure de l'ordinateur et aux fonctions gérant le temps.

* `urllib`<fnref n="10" /> : récupération de données sur internet depuis Python.

* `Tkinter`<fnref n="11" /> : interface python avec Tk. Création d'objets graphiques (voir chapitre 25 *Fenêtres graphiques et Tkinter* (en ligne)).

* `re`<fnref n="12" /> : gestion des expressions régulières (voir chapitre 17 *Expressions régulières et parsing*).

Nous vous conseillons d'aller explorer les pages de ces modules pour découvrir toutes leurs potentialités.

<footnote n="4">4. https://docs.python.org/fr/3/py-modindex.html</footnote>

<footnote n="5">5. https://docs.python.org/fr/3/library/math.html#module-math</footnote>

<footnote n="6">6. https://docs.python.org/fr/3/library/sys.html#module-sys</footnote>

<footnote n="7">7. https://docs.python.org/fr/3/library/os.html#module-os</footnote>

<footnote n="8">8. https://docs.python.org/fr/3/library/random.html#module-random</footnote>

<footnote n="9">9. https://docs.python.org/fr/3/library/time.html#module-time</footnote>

<footnote n="10">10. https://docs.python.org/fr/3/library/urllib.html#module-urllib</footnote>

<footnote n="11">11. https://docs.python.org/fr/3/library/tkinter.html#module-tkinter</footnote>

<footnote n="12">12. https://docs.python.org/fr/3/library/re.html#module-re</footnote>
Vous verrez dans le chapitre 15 *Création de modules* comment créer votre propre module lorsque vous souhaitez réutiliser souvent vos propres fonctions.

Enfin, notez qu'il existe de nombreux autres modules externes qui ne sont pas installés de base dans Python, mais qui sont très utilisés en bioinformatique et en analyse de données. Par exemple : *NumPy* (manipulations de vecteurs et de matrices), *Biopython* (manipulation de séquences ou de structures de biomolécules), * matplotlib* (représentations graphiques), *pandas* (analyse de données tabulées), etc. Ces modules vous serons présentés dans les chapitres 19 à 22.

## 9.5 Module random : génération de nombres aléatoires

Comme indiqué précédemment, le module `random`<fnref n="13" /> contient des fonctions pour la génération de nombres aléatoires :

```python
1 >>> import random
2 >>> randomrandint(0, 10)
3 4
4 >>> randomrandint(0, 10)
5 10
6 >>> randomuniform(0, 10)
7 6.574743184892878
8 >>> randomuniform(0, 10)
9 1.1655547702189106
```

Le module `random` permet aussi de permuter aléatoirement des listes :

```python
1 >>> x = [1, 2, 3, 4]
2 >>> random shufflex
3 >>> x
4 [2, 3, 1, 4]
5 >>> random shufflex
6 >>> x
7 [4, 2, 1, 3]
```

Mais aussi de tirer aléatoirement un élément dans une liste donnée :

```python
1 >>> bases = ["A", "T", "C", "G"]
2 >>> random.choice(bases)
3 'A'
4 >>> random.choice(bases)
5 'G'
```

La fonction `choices()` (avec un `s` à la fin) réalise plusieurs tirages aléatoires (avec remise, c'est-à-dire qu'on peut piocher plusieurs fois le même élément) dans une liste donnée. Le nombre de tirages est précisé par le paramètre `k` :

```python
1 >>> random.choice(bases, k=5)
2 ['G', 'A', 'A', 'T', 'G']
3 >>> random.choice(bases, k=5)
4 ['A', 'T', 'A', 'A', 'C']
5 >>> random.choice(bases, k=10)
6 ['C', 'T', 'T', 'T', 'G', 'A', 'C', 'A', 'G', 'G']
```

Si vous exécutez vous-même les exemples précédents, vous devriez obtenir des résultats légèrement différents de ceux indiqués.

Pour des besoins de reproductibilité des analyses en science, on a souvent besoin de retrouver les mêmes résultats même si on utilise des nombres aléatoires. Pour cela, on peut définir ce qu'on appelle la « graine aléatoire ».

### Définition

En informatique, la génération de nombres aléatoires est un problème complexe. On utilise plutôt des « générateurs de nombres pseudo-aléatoires<fnref n="14" /> ». Pour cela, une graine aléatoire<fnref n="15" /> doit être définie. Cette graine est la plupart du temps un nombre entier qu'on passe au générateur : celui-ci va alors produire une série donnée de nombres pseudo-aléatoires qui dépendent de cette graine. Si on change la graine, la série de nombres change.

<footnote n="13">13. https://docs.python.org/fr/3/library/random.html#module-random</footnote>

<footnote n="14">14. https://fr.wikipedia.org/wiki/G%C3%A9n%C3%A9rateur_de_nombres_pseudo-al%C3%A9atoires</footnote>

<footnote n="15">15. https://fr.wikipedia.org/wiki/Graine_al%C3%A9atoire</footnote>
En Python, la graine aléatoire se définit avec la fonction `seed()` :

1 >>> random.seed(42)

2 >>> random.randint(0, 10)

3 1

4 >>> random.random(0, 10)

5 0

6 >>> random.random(0, 10)

7 4

Ici, la graine aléatoire est fixée à 42. Si on ne précise pas la graine, par défaut Python utilise la date (plus précisément, il s'agit du nombre de secondes écoulées depuis une date fixe passée). Ainsi, à chaque fois qu'on relance Python, la graine sera différente, car ce nombre de secondes sera différent.

Si vous exécutez ces mêmes lignes de code (depuis l'instruction `random.seed(42)`), il se peut que vous ayez des résultats différents selon la version de Python. Néanmoins, vous devriez systématiquement obtenir les mêmes résultats si vous relancez plusieurs fois de suite ces instructions sur une même machine.

### Remarque

Quand on utilise des nombres aléatoires, il est fondamental de connaître la distribution de probabilités utilisée par la fonction.

Par exemple, la fonction de base du module `random` est `random.random()`, elle renvoie un `float` aléatoire entre 0 et 1 tiré dans une **distribution uniforme**. Si on tire beaucoup de nombres, on aura la même probabilité d'obtenir tous les nombres possibles entre 0 et 1. La fonction `random.random()` tire aussi un entier dans une **distribution uniforme**. La fonction `random.gauss()` tire quant à elle un `float` aléatoire dans une **distribution gaussienne**.

## 9.6 Module sys : passage d'arguments

Le module `sys`<fnref n="16" /> contient des fonctions et des variables spécifiques à l'interpréteur Python lui-même.

Ce module est particulièrement intéressant pour récupérer les arguments passés à un script Python lorsque celui-ci est appelé en ligne de commande.

Dans cet exemple, créons le script suivant que l'on enregistrera sous le nom `test.py` :

1 import sys

2 print(sys.argv)

Ensuite, dans un shell, exécutons le script `test.py` suivi de plusieurs arguments :

```
$ python test.py salut girafe 42
['test.py', 'salut', 'girafe', '42']
```

* **Ligne 1.** Le caractère `$` représente l' invite du shell, `test.py` est le nom du script Python, `salut`, `girafe` et 42 sont les arguments passés au script (tous séparés par un espace).

* **Ligne 2.** Le script affiche le contenu de la variable `sys.argv`. Cette variable est une liste qui contient tous les arguments de la ligne de commande, y compris le nom du script Python lui-même qu'on retrouve comme premier élément de cette liste dans `sys.argv[0]`. On peut donc accéder à chacun des différents arguments du script avec `sys.argv[1]`, `sys.argv[2]`, etc.

Toujours dans le module `sys`, la fonction `sys.exit()` est utile pour quitter un script Python. On peut donner un argument à cette fonction (en général une chaîne de caractères) qui sera renvoyé au moment où Python quittera le script. Par exemple, si vous attendez au moins un argument en ligne de commande, vous pouvez renvoyer un message pour indiquer à l'utilisateur ce que le script attend comme argument :

1 import sys

2

3 if len(sys.argv) != 2:

4 sys.exit("ERREUR : il faut exactement un argument.")

5

6 print(f"Argument vaut : {sys.argv[1]}")

<footnote n="16">16. https://docs.python.org/fr/3/library/sys.html#module-sys</footnote>
Puis on l'exécute sans argument :

```
$ python test.py
ERREUR : il faut exactement un argument.
```

avec un seul argument :

```
$ python test.py 42
Argument vaut : 42
```

puis avec plusieurs :

```
$ python test.py 42 salut
ERREUR : il faut exactement un argument.
```

## Remarque

On vérifie dans cet exemple que le script possède deux arguments, car le nom du script lui-même compte pour un argument (le tout premier).

L'intérêt de récupérer des arguments passés dans la ligne de commande à l'appel du script est de pouvoir ensuite les utiliser dans le script.

Voici comme nouvel exemple le script compte_lignes.py, qui prend comme argument le nom d'un fichier puis affiche le nombre de lignes qu'il contient :

```python
1 import sys
2
3 if len(sys.argv) != 2:
4     sys.exit("ERREUR : il faut exactement un argument.")
5
6 nom_fichier = sys.argv[1]
7 taille = 0
8 with open(nom_fichier, "r") as f_in:
9     taille = len(f_in.readline())
10
11 print(f"{nom_fichier} contient {taille} lignes.")
```

Supposons que dans le même répertoire, nous ayons le fichier animaux1.txt dont voici le contenu :

```
girafe
tigre
singe
souris
```

et le fichier animaux2.txt qui contient :

```
poisson
abeille
chat
```

Utilisons maintenant le script compte_lignes.py :

```
$ python compte_lignes.py
ERREUR : il faut exactement un argument.
$ python compte_lignes.py animaux1.txt
 animaux1.txt contient 4 lignes.
$ python compte_lignes.py animaux2.txt
 animaux2.txt contient 3 lignes.
$ python compte_lignes.py animaux1.txt animaux2.txt
ERREUR : il faut exactement un argument.
```

Notre script est donc capable de :

* vérifier si un argument lui est donné et si ce n'est pas le cas d'afficher un message d'erreur ;

* d'ouvrir le fichier dont le nom est fourni en argument, de compter puis d'afficher le nombre de lignes.
Par contre, le script ne vérifie pas si le fichier fourni en argument existe bien :

```python
$ python compte_lignes.py animaux3.txt
Traceback (most recent call last):
  File "compte_lignes.py", line 8, in <module>
    with open(nom_fichier, "r") as f_in:
=========================
FileNotFoundError: [Errno 2] No such file or directory: 'animaux3.txt'
```

La lecture de la partie suivante va nous permettre d'améliorer notre script `compte_lignes.py`.

## 9.7 Module `pathlib`: gestion des fichiers et des répertoirs

Le module `pathlib`<fnref n="17" /> permet de manipuler les fichiers et les répertoirs.

Le plus souvent, on utilise uniquement la classe `Path` du module `pathlib`, qu'on charge de cette manière :

```python
1 >>> from urllib import Path
```

La méthode `.exists()` vérifie la présence d'un fichier sur le disque dur :

```python
1 >>> import sys
2 >>> from urllib import Path
3 >>> if Path("toto.pdb").exists():
4 ...     print("le fichier est présent")
5 ... else:
6 ...     sys.exit("le fichier est absent")
7 ...
8 le fichier est absent
```

Dans cet exemple, si le fichier n'existe pas sur le disque dur, on quitte le programme avec la fonction `exit()` du module `sys` que nous venons de voir.

La méthode `.cwd()` renvoie le chemin complet du répertoire depuis lequel est lancé Python (cwd signifiant current working directory) :

```python
1 >>> from urllib import Path
2 >>> Path().cwd()
3 PosixPath('/home/pierre')
```

On obtient un objet de type `PosixPath` qu'il est possible de transformer si besoin en chaîne de caractères avec la fonction `str()`, que nous avons vu dans le chapitre 2 *Variables* :

```python
1 >>> str(Path().cwd())
2 '/home/pierre'
```

Mais l'intérêt de récupérer un objet de type `PosixPath` est qu'on peut ensuite utiliser les méthodes `.name` et `.parent` pour obtenir respectivement le nom du répertoire (sans son chemin complet) et le répertoire parent :

```python
1 >>> Path().cwd()
2 PosixPath('/home/pierre')
3 >>> Path().cwd().name
4 'pierre'
5 >>> Path().cwd().parent
6 PosixPath('/home')
```

Enfin, la méthode `.iterdir()` liste le contenu du répertoire depuis lequel est lancé Python :

```python
1 >>> list(Path().iterdir())
2 [PosixPath('demo.py'), PosixPath('tests'), PosixPath('1BTA.pdb')]
```

Tout comme la fonction `range()` (voir le chapitre 4 *Listes*), la méthode `.iterdir()` est un itérateur. La fonction `list()` permet d'obtenir une liste.

Toutefois, il est possible d'itérer très facilement sur le contenu d'un répertoire et de savoir s'il contient des fichiers ou des sous-répertoirs :

<footnote n="17">17. https://docs.python.org/fr/3/library/pathlib.html</footnote>
1 >>> for nom in Path().iterdir():

2 ... if nom.is_file():

3 ... print(f"{nom} est un fichier")

4 ... else:

5 ... print(f"{nom} n'est pas un fichier")

6 ...

7 demo.py est un fichier

8 tests n'est pas un fichier

9 1BTA.pdb est un fichier

La méthode .is_file() renvoie True si l'objet est un fichier, et False si ce n'est pas le cas.

La méthode .iterdir() parcourt le contenu d'un répertoire, sans en explorer les éventuels sous-répertoires. Si on souhaite parcourir récursivement un répertoire, on utilise la méthode .glob(). Prenons l'arborescence suivante comme exemple :

1BTA.pdb

demo.py

tests

results.csv

script1.py

script2.py

Le répertoire courant contient les fichiers 1BTA.pdb et demo.py, ainsi que le répertoire tests. Ce dernier contient lui-même les fichiers results.csv, script1.py et script2.py.

On souhaite maintenant lister tous les scripts Python (dont l'extension est .py) présents dans le répertoire courant et dans ses sous-répertoires :

1 >>> for nom in Path().glob("**/*.py"):

2 ... print(f"{nom}")

3 ...

4 demo.py

5 tests/script1.py

6 tests/script2.py

Dans la chaîne de caractères "**/*.py", ** recherche tous les sous-répertoires récursivement et *.py signifie n'importe quel nom de fichier qui se termine par l'extension .py.

Il existe de nombreuse autres méthodes associées à la classe Path du module pathlib, n'hésitez pas à consulter la documentation<fnref n="18" />.

## 9.8 Exercices

### Conseil

Pour les trois premiers exercices, utilisez l'interpréteur Python. Pour les exercices suivants, créez des scripts puis exécutez-les dans un shell.

### 9.8.1 Racine carrée

Affichez sur la même ligne les nombres de 10 à 20 (inclus) ainsi que leur racine carrée avec trois décimales. Utilisez pour cela le module math avec la fonction sqrt(). Exemple :

10 3.162

11 3.317

12 3.464

13 3.606

[...]

Consultez pour cela la documentation<fnref n="19" /> de la fonction mathmathematics. square root.

<footnote n="18">18. https://docs.python.org/3/library/pathlib.html</footnote>

<footnote n="19">19. https://docs.python.org/fr/3/library/math.html#math Round</footnote>
### 9.8.2 Cosinus

Calculez le cosinus de $\pi/2$ en utilisant le module math avec la fonction cos() et la constante pi.

Consultez pour cela la documentation<sup>20</sup> de la fonction math.cos() et la documentation<sup>21</sup> de la constante math.pi.

### 9.8.3 Comparaison de floats

Montrez que

$$ \sqrt{5 + \sqrt{4 + \sqrt{3}}} $$

est égale à e à 0,001 près.

Montrez ensuite que

$$ \sqrt{7 + \sqrt{6 + \sqrt{5}}} $$

est égale à π à 0,0001 près.

**Conseil**

* Jetez un oeil à la rubrique sur la comparaison de floats abordée dans le chapitre 6 Tests.

* Les constantes π et e sont obtenues par math.pi et math.e.

### 9.8.4 Chemin et contenu du répertoire courant

Affichez le chemin et le contenu du répertoire courant (celui depuis lequel vous avez lancé l'interpréteur Python).

Déterminez également le nombre total de fichiers et de répertoires présents dans le répertoire courant.

### 9.8.5 Affichage temporisé

Affichez les nombres de 1 à 10 avec 1 seconde d'intervalle. Utilisez pour cela le module time et sa fonction sleep().

Consultez pour cela la documentation<sup>22</sup> de la fonction time.sleep().

### 9.8.6 Séquences aléatoires de chiffres

Générez une séquence aléatoire de six chiffres, ceux-ci étant des entiers tirés entre 1 et 4. Utilisez le module random avec la fonction$_{randint()}$.

Consultez pour cela la documentation<sup>23</sup> de la fonction$_{randomrandint()}$.

### 9.8.7 Compteur de points de jeu de belote

On considère un jeu de belote avec la variante sans-atout, où chaque carte vaut un certain nombre de points quelle que soit sa couleur (trèfle, carreau, coeur, pique). Un dictionnaire permet de mettre la correspondance entre chaque carte et son nombre de points :

```
1 # Nombre de points de chaque carte.
2 # (V = valet, D = dame, R = roi, # d = 10, A = as).
3 dico_points_sans_atouts = {"7": 0, "8": 0, "9": 0, "V": 2, "D": 3,
4                         "R": 4, "d": 10, "A": 11}
```

Par ailleurs, on peut représenter un jeu de 32 cartes par une liste :

```
jeuCartes = ["7", "8", "9", "d", "V", "D", "R", "A"] * 4
```

Créez un programme belote.py qui tire huit cartes au hasard sans remise et qui affiche le nombre de points correspondant. Pour cela, vous pouvez utiliser la fonction$_{random.sample()}$ et son argument par mot-clé k. N'hésitez pas à consulter la documentation<sup>24</sup>. On souhaite une sortie de ce style :

<footnote n="20">20. https://docs.python.org/fr/3/library/math.html#math.cos</footnote>

<footnote n="21">21. https://docs.python.org/fr/3/library/math.html#math.pi</footnote>

<footnote n="22">22. https://docs.python.org/fr/3/library/time.html#time.sleep</footnote>

<footnote n="23">23. https://docs.python.org/fr/3/library/random.html#random.randomint</footnote>

<footnote n="24">24. https://docs.python.org/fr/3/library/random.html#random.sample</footnote>
```python
$ python belote.py
La main est ['7', 'A', '9', 'A', 'R', 'd', 'V', 'D'].
7 --> 0 points
A --> 11 points
9 --> 0 points
A --> 11 points
R --> 4 points
d --> 10 points
V --> 2 points
D --> 3 points
Le nombre total de points de la main est 41.
```

### 9.8.8 Séquences aléatoires d'ADN

Générez une séquence aléatoire d'ADN de 20 bases de deux manières différentes. Utilisez le module `random` avec la fonction `choice()` ou `choices()`.

### 9.8.9 Séquences aléatoires d'ADN avec argument

Créez un script `dna_random.py` qui prend comme argument un nombre de bases, construit une séquence aléatoire d'ADN dont la longueur est le nombre de bases fourni en argument, puis affiche cette séquence.

Le script devra vérifier qu'un argument est bien fourni et renvoyer un message d'erreur si ce n'est pas le cas.

#### Conseil

Pour générer la séquence d'ADN, utilisez la fonction `random.choice()` abordée dans l'exercice précédent.

### 9.8.10 Compteur de lignes

Améliorez le script `compte_lignes.py`, dont le code a été donné précédemment, de façon à ce qu'il renvoie un message d'erreur si le fichier n'existe pas.

Par exemple, si les fichiers `animaux1.txt` et `animaux2.txt` sont bien dans le répertoire courant, mais pas `animaux3.txt`:

```bash
$ python compte_lignes.py animaux1.txt
animaux1.txt contient 4 lignes.
$ python compte_lignes.py animaux2.txt
animaux2.txt contient 3 lignes.
$ python compte_lignes.py animaux3.txt
ERREUR : animaux3.txt n'existe pas.
```

### 9.8.11 Détermination du nombre pi par la méthode Monte Carlo (exercice +++)

Soit un cercle de rayon 1 (en trait plein sur la figure 9.1) inscrit dans un carré de côté 2 (en trait pointillé).

Avec $R = 1$, l'aire du carré vaut $(2R)^2$ soit 4 et l'aire du disque délimité par le cercle vaut $\pi R^2$ soit $\pi$.

En choisissant $N$ points aléatoires (à l'aide d'une distribution uniforme) à l'intérieur du carré, la probabilité que ces points se trouvent aussi dans le cercle est :

$$p = \frac{\text{aire du cercle}}{\text{aire du carré}} = \frac{\pi}{4}$$

Soit $n$, le nombre de points effectivement dans le cercle, il vient alors

$$p = \frac{n}{N} = \frac{\pi}{4}$$

d'où

$$\pi = 4 \times \frac{n}{N}$$

Déterminez une approximation de $\pi$ par cette méthode. Pour cela, pour $N$ itérations :
<figure><img src="image_12.png" /><figcaption>FIGURE 9.1 – Cercle de rayon 1 inscrit dans un carré de côté 2.</figcaption></figure>

1. Choisissez aléatoirement les coordonnées x et y d'un point entre -1 et 1. Utilisez la fonction `uniform()` du module `random`.

2. Calculez la distance entre le centre du cercle et ce point.

3. Déterminez si cette distance est inférieure au rayon du cercle, c'est-à-dire si le point est dans le cercle ou pas.

4. Si le point est effectivement dans le cercle, incrémentez le compteur $n$.

Finalement calculez le rapport entre $n$ et $N$ et proposez une estimation de $\pi$. Quelle valeur de $\pi$ obtenez-vous pour 100 itérations ? 1000 itérations ? 10 000 itérations ? Comparez les valeurs obtenues à la valeur de $\pi$ fournie par le module `math`.

On rappelle que la distance $d$ entre deux points A et B de coordonnées respectives $(x_A, y_A)$ et $(x_B, y_B)$ se calcule comme :

$$d = \sqrt{(x_B - x_A)^2 + (y_B - y_A)^2}$$

Pour vous aider, consultez la documentation<sup>25</sup> de la fonction `random uniform()`.

<footnote n="25">25. https://docs.python.org/fr/3/library/random.html#random/uniform</footnote>
# 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 `math.cos(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><img src="image_13.png" /><figcaption>FIGURE 10.1 – Fonctionnement schématique d'une fonction.</figcaption></figure>

Par exemple, si vous appelez la fonction `len()` de la manière suivante :
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 `math.cos()` 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 :
```python
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 :

```python
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 :

```python
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 :

```python
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 :
```python
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 :

```python
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 :

```python
1 def est_pair(x):
2     if x % == 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 ?

```python
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 :
```python
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é :

```python
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 :

```python
1 >>> fct(z=10)
2 (0, 0, 10)
```

Python permet même de rentrer les arguments par mot-clé dans un ordre arbitraire :

```python
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é :

```python
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.

```python
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'
```
### 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*<fnref n="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.

<figure><img src="image_14.png" /></figure>

<footnote n="1">1. http://www.python tutor.com</footnote>
* **É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.

<figure><img src="image_15.png" /></figure>

* **É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*.

<figure><img src="image_16.png" /></figure>

* **É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à.
<figure><img src="image_17.png" /></figure>

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

<figure><img src="image_18.png" /></figure>

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

<figure><img src="image_19.png" /></figure>

* É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.
<figure><img src="image_20.png" /></figure>

* **É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.

<figure><img src="image_21.png" /></figure>

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

<figure><img src="image_22.png" /></figure>

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*.