From c60cdf06ad04798fed1a5775fbb5a2671fb37559 Mon Sep 17 00:00:00 2001 From: Dimitri Merejkowsky Date: Wed, 12 Feb 2020 14:13:35 +0100 Subject: [PATCH] Proofread, convert to sphinx --- cours/build.py | 4 +- .../01-introduction/01-introduction.rst | 14 +- .../01-introduction/02-le-langage-python.rst | 25 +- cours/source/01-introduction/index.rst | 13 +- .../01-bases-ligne-de-commandes.rst | 14 +- .../02-premiers-pas/02-interpréteur.rst | 38 +- .../02-premiers-pas/03-maths-simples.rst | 82 ++-- cours/source/02-premiers-pas/index.rst | 13 +- .../03-variables-et-types/01-variables.rst | 52 +- .../02-chaînes-de-caractères.rst | 65 ++- .../source/03-variables-et-types/03-types.rst | 47 +- .../03-variables-et-types/04-booléens.rst | 45 +- cours/source/03-variables-et-types/index.rst | 14 +- .../source/04-code-source/01-code-source.rst | 107 ----- cours/source/04-code-source/index.rst | 102 +++- .../01-flot-de-contrôle.rst | 48 +- .../05-flot-de-controle/02-exercice.rst | 33 +- cours/source/05-flot-de-controle/index.rst | 12 +- cours/source/06-fonctions/01-functions.rst | 76 ++- .../06-fonctions/02-portée-des-variables.rst | 46 +- .../06-fonctions/03-plusieurs-arguments.rst | 54 +-- cours/source/06-fonctions/04 | 1 - cours/source/06-fonctions/04-par-défaut.rst | 39 +- .../06-fonctions/05-fonctions-natives.rst | 50 +- cours/source/06-fonctions/06-return.rst | 77 ++- cours/source/06-fonctions/index.rst | 16 +- cours/source/07-listes/01-listes.rst | 164 ------- cours/source/07-listes/index.rst | 154 +++++- cours/source/08-none-et-pass/01-none.rst | 160 +++---- cours/source/08-none-et-pass/02-pass.rst | 35 +- cours/source/08-none-et-pass/index.rst | 12 +- .../09-dictionnaires/01-dictionnaires.rst | 205 -------- cours/source/09-dictionnaires/index.rst | 180 ++++++- cours/source/10-tuples/01-tuples.rst | 169 ------- cours/source/10-tuples/index.rst | 155 +++++- cours/source/11-classes-01/01-classes.rst | 319 ------------- cours/source/11-classes-01/index.rst | 300 +++++++++++- cours/source/12-modules-01/01-modules.rst | 148 ------ cours/source/12-modules-01/index.rst | 139 +++++- cours/source/13-classes-02/01-rappels.rst | 265 +++++------ cours/source/13-classes-02/02-couplage.rst | 139 +++--- cours/source/13-classes-02/03-composition.rst | 139 +++--- cours/source/13-classes-02/index.rst | 13 +- .../14-bibliothèques-01/01-rappels.rst | 43 +- .../14-bibliothèques-01/02-sys.path.rst | 446 ++---------------- .../03-bibliotheques-tierces.rst | 312 +++++++++--- .../14-bibliothèques-01/04-dépendances.rst | 207 ++++---- .../14-bibliothèques-01/05-virtualenv.rst | 209 ++++---- cours/source/14-bibliothèques-01/index.rst | 16 +- cours/source/conf.py | 1 + cours/source/index.rst | 30 +- 51 files changed, 2331 insertions(+), 2716 deletions(-) delete mode 100644 cours/source/04-code-source/01-code-source.rst delete mode 100644 cours/source/06-fonctions/04 delete mode 100644 cours/source/07-listes/01-listes.rst delete mode 100644 cours/source/09-dictionnaires/01-dictionnaires.rst delete mode 100644 cours/source/10-tuples/01-tuples.rst delete mode 100644 cours/source/11-classes-01/01-classes.rst delete mode 100644 cours/source/12-modules-01/01-modules.rst diff --git a/cours/build.py b/cours/build.py index 91853aa..20fc3b7 100755 --- a/cours/build.py +++ b/cours/build.py @@ -6,9 +6,11 @@ def main(): dev = "--dev" in sys.argv if dev: program = "sphinx-autobuild" + opts = [] else: program = "sphinx-build" - cmd = [program, "-d", "build", "-b", "html", "source", "build/html"] + opts = ["-W"] + cmd = [program, *opts, "-d", "build", "-b", "html", "source", "build/html"] subprocess.run(cmd, check=True) diff --git a/cours/source/01-introduction/01-introduction.rst b/cours/source/01-introduction/01-introduction.rst index 603cc05..68255c6 100644 --- a/cours/source/01-introduction/01-introduction.rst +++ b/cours/source/01-introduction/01-introduction.rst @@ -1,19 +1,17 @@ -+++ -title = "Objet de ce livre" -weight = 1 -+++ - -# Objet de ce livre +Objet de ce livre +================= Apprendre la programmation en partant de rien, en utilisant Python et la ligne de commande -# Pourquoi Python? +Pourquoi Python? +---------------- * Excellent langage pour débuter * Mon langage préféré * Vraiment cross-platform (sauf pour le mobile) -# Pourquoi la ligne de commande? +Pourquoi la ligne de commande? +------------------------------ Interface universelle diff --git a/cours/source/01-introduction/02-le-langage-python.rst b/cours/source/01-introduction/02-le-langage-python.rst index c358f3f..fb099d4 100644 --- a/cours/source/01-introduction/02-le-langage-python.rst +++ b/cours/source/01-introduction/02-le-langage-python.rst @@ -1,11 +1,8 @@ -+++ -title = "Le langage Python" -weight = 3 -+++ +Présentation du langage Python +============================== -# Présentation du langage Python - -## Utilisation de Python +Utilisation de Python +---------------------- * Aussi appelé "langage de script", `glue language` @@ -19,7 +16,8 @@ weight = 3 * Ligne de commande * ... -## Petit détour: version d'un programme +Petit détour: version d'un programme +------------------------------------ * Comme les versions d'un document * Si le nombre est plus grand, c'est plus récent @@ -29,7 +27,8 @@ weight = 3 * `1.5.1 -> 4.3`: beaucoup de changements * On omet souvent le reste des numéros quand c'est pas nécessaire -## Historique +Historique +---------- * Créé par Guido van Rossum. Conçu à la base pour l'enseignement. * Le nom vient des Monty Python (si, si) @@ -37,7 +36,8 @@ weight = 3 * Python 2: en 2000 * Python 3: en 2008 -## Le grand schisme +Le grand schisme +---------------- La plupart des langages continuent à être compatibles d'une version à l'autre. @@ -45,10 +45,9 @@ La plupart des langages continuent à être compatibles d'une version à l'autre Heureusement, 10 ans plus tard, la situation s'est arrangée, et Python2 cessera d'être maintenu le premier janvier 2020. -## Python3 +Python3 +------- Ce cours fonctionne donc uniquement avec Python3. N'utilisez *pas* Python2, sinon certaines choses expliquées ici ne marcheront pas :/ - - diff --git a/cours/source/01-introduction/index.rst b/cours/source/01-introduction/index.rst index 2cbafb5..7e5bc65 100644 --- a/cours/source/01-introduction/index.rst +++ b/cours/source/01-introduction/index.rst @@ -1,4 +1,9 @@ -+++ -title = "Chapitre 1 - Introduction" -weight = 1 -+++ +Chapitre 1 - Introduction +========================= + + +.. toctree:: + :maxdepth: 1 + + 01-introduction + 02-le-langage-python diff --git a/cours/source/02-premiers-pas/01-bases-ligne-de-commandes.rst b/cours/source/02-premiers-pas/01-bases-ligne-de-commandes.rst index fb6dc53..6902201 100644 --- a/cours/source/02-premiers-pas/01-bases-ligne-de-commandes.rst +++ b/cours/source/02-premiers-pas/01-bases-ligne-de-commandes.rst @@ -1,11 +1,8 @@ -+++ -title = "Ligne de commande" -weight = 2 -+++ +La ligne de commande +=================== -# La ligne de commande - -## Pourquoi la ligne de commande? +Pourquoi la ligne de commande? +------------------------------ * Très puissant * Ancien, mais toujours d'actualité @@ -13,7 +10,8 @@ weight = 2 * Écrire des programmes qui marche dans la ligne de commande est (relativement) simple * Possibilités infines, même si on ne fait que manipuler du texte -## Les bases +Les bases +---------- On tape un commande, on appuie sur entrée, l'ordinateur interprète ce qui a été tapé et affiche un message: diff --git a/cours/source/02-premiers-pas/02-interpréteur.rst b/cours/source/02-premiers-pas/02-interpréteur.rst index f4ce3b9..0241012 100644 --- a/cours/source/02-premiers-pas/02-interpréteur.rst +++ b/cours/source/02-premiers-pas/02-interpréteur.rst @@ -1,30 +1,30 @@ -+++ -title = "L'interpréteur interactif" -weight = 4 -+++ -# L'interpréteur interactif +L'interpréteur interactif +========================= -## Installation +Installation +------------ Il se lance depuis l'invite de commande du système d'exploitation: -``` -$ python3 -Python 3.7.1 (default, Oct 22 2018, 10:41:28) -[GCC 8.2.1 20180831] on linux -Type "help", "credits" or "license" for more information. ->>> -``` +.. code-block:: console -## Deux invites de commandes + $ python3 + Python 3.7.1 (default, Oct 22 2018, 10:41:28) + [GCC 8.2.1 20180831] on linux + Type "help", "credits" or "license" for more information. + >>> -Notez les trois chevrons: `>>>`. Cela vous permet de différencier l'invite +Deux invites de commandes +------------------------- + +Notez les trois chevrons: ``>>>``. Cela vous permet de différencier l'invite de commandes du système d'exploitation de celle de Python. -* Système d'exploitation -> Python: taper `python3` (sans arguments) -* Python -> Système d'exploitation: taper `quit()` +* Système d'exploitation -> Python: taper ``python3`` (sans arguments) +* Python -> Système d'exploitation: taper ``quit()`` -## Note +Note +----- À partir de maintenant, recopiez les entrées sur les slides dans votre propre @@ -32,4 +32,4 @@ interpréteur. Vous devez taper la même chose après l'invite de commande ('>>>') et vous devez voir les mêmes réponses. -Si ce n'est pas le cas, relisez attentitivement ce que vous avez tapé, sinon [contactez-moi](https://dmerej.info/blog/fr/pages/about/). +Si ce n'est pas le cas, relisez attentitivement ce que vous avez tapé, sinon `contactez-moi `_. diff --git a/cours/source/02-premiers-pas/03-maths-simples.rst b/cours/source/02-premiers-pas/03-maths-simples.rst index 26c8b1c..34bc79e 100644 --- a/cours/source/02-premiers-pas/03-maths-simples.rst +++ b/cours/source/02-premiers-pas/03-maths-simples.rst @@ -1,62 +1,62 @@ -+++ -title = "Maths simples" -weight = 5 -+++ +Maths simples +============= -# Maths simples +Opérations avec des entiers +--------------------------- -## Opérations avec des entiers +.. code-block:: python -``` ->>> 1 -1 ->>> 2 -2 ->>> 1 + 2 -3 ->>> 2 * 3 -6 -``` + >>> 1 + 1 + >>> 2 + 2 + >>> 1 + 2 + 3 + >>> 2 * 3 + 6 -## Opérantions avec des flottants +Opérations avec des flottants +----------------------------- -C'est le `.` qui fait le flottant +C'est le ``.`` qui fait le flottant -``` ->>> 0.5 -0.5 ->>> 0.5 + 0.2 -0.7 ->>> 10 / 3 -3.3333333333333335 -``` + +.. code-block:: python + + >>> 0.5 + 0.5 + >>> 0.5 + 0.2 + 0.7 + >>> 10 / 3 + 3.3333333333333335 *Note: les flottants sont imprécis* -## Division entières et modulo +Division entières et modulo +--------------------------- + +.. code-block:: python -``` ->>> 14 // 3 -4 ->>> 14 % 3 -2 -``` + >>> 14 // 3 + 4 + >>> 14 % 3 + 2 *Le `%` n'a rien à voir avec un pourcentage!* -## Priorité des opérations +Priorité des opérations +------------------------ -``` ->>> 1 + 2 * 3 -7 ->>> (1 + 2) * 3 -9 -``` +.. code-block:: python -*Les parenthèses permettent de grouper les expressions* + >>> 1 + 2 * 3 + 7 + >>> (1 + 2) * 3 + 9 +*Les parenthèses permettent de grouper les expressions* diff --git a/cours/source/02-premiers-pas/index.rst b/cours/source/02-premiers-pas/index.rst index b955a90..914d0c2 100644 --- a/cours/source/02-premiers-pas/index.rst +++ b/cours/source/02-premiers-pas/index.rst @@ -1,4 +1,9 @@ -+++ -title = "Chapitre 2 - Premiers pas" -weight = 2 -+++ +Chapitre 2 - Premiers pas +========================= + +.. toctree:: + :maxdepth: 1 + + 01-bases-ligne-de-commandes + 02-interpréteur.rst + 03-maths-simples.rst diff --git a/cours/source/03-variables-et-types/01-variables.rst b/cours/source/03-variables-et-types/01-variables.rst index 5e9a366..5ba55a5 100644 --- a/cours/source/03-variables-et-types/01-variables.rst +++ b/cours/source/03-variables-et-types/01-variables.rst @@ -1,31 +1,27 @@ -+++ -title = "Variables" -weight = 6 -+++ - -# Variables - -```python ->>> a = 2 ->>> a -2 ->>> b = 3 ->>> a + b -5 -``` +Variables +========= + +.. code-block:: python + + >>> a = 2 + >>> a + 2 + >>> b = 3 + >>> a + b + 5 * On peut nommer des valeurs * On peut afficher la valeur d'une variable entrant son nom dans le REPL -```python ->>> a = 2 ->>> a -2 ->>> a = 3 ->>> a -3 -``` +.. code-block:: python + + >>> a = 2 + >>> a + 2 + >>> a = 3 + >>> a + 3 * On peut changer la valeur d'une variable (d'où son nom) @@ -34,7 +30,8 @@ weight = 6 variables par leurs valeurs -## Nom des variables +Nom des variables +----------------- Préférez des noms longs et descriptifs @@ -42,8 +39,7 @@ Toujours en minuscules Séparez les "mots" par des tirets bas (underscore) -```python ->>> score = 42 ->>> age_moyen = 22 -``` +.. code-block:: python + score = 42 + age_moyen = 22 diff --git a/cours/source/03-variables-et-types/02-chaînes-de-caractères.rst b/cours/source/03-variables-et-types/02-chaînes-de-caractères.rst index 71d54c4..4a881b5 100644 --- a/cours/source/03-variables-et-types/02-chaînes-de-caractères.rst +++ b/cours/source/03-variables-et-types/02-chaînes-de-caractères.rst @@ -1,60 +1,57 @@ -+++ -title = "Chaînes de caractères" -weight = 7 -+++ - -# Chaînes de caractères +Chaînes de caractères +====================== Aussi appelées "string". -Avec des simple quotes (`'`) +Avec des simple quotes (``'``) + +.. code-block:: python -```python ->>> 'Bonjour monde!' -'Bonjour monde!' -``` + >>> 'Bonjour monde!' + 'Bonjour monde!' Marche aussi avec des double quotes (`"`) -```python ->>> "Bonjour, monde!" -'Bonjour monde' -``` +.. code-block:: python -## Double et simple quotes + >>> "Bonjour, monde!" + 'Bonjour monde' + +Double et simple quotes +----------------------- On peut mettre des simples quotes dans des double quotes et vice-versa. -```python ->>> "Il a dit: 'bonjour' ce matin." -"Il a dit: 'bonjour' ce matin." +.. code-block:: python + + >>> "Il a dit: 'bonjour' ce matin." + "Il a dit: 'bonjour' ce matin." ->>> 'Il a dit: "bonjour" ce matin' -'Il a dit: "bonjour" ce matin!' -``` + >>> 'Il a dit: "bonjour" ce matin' + 'Il a dit: "bonjour" ce matin!' -## Échappement +Échappement +----------- Avec la barre oblique inversée "backslash" -```python ->>> 'Il a dit: "bonjour". C\'est sympa!' -'Il a dit: "bonjour". C\'est sympa!' -``` +.. code-block:: python + >>> 'Il a dit: "bonjour". C\'est sympa!' + 'Il a dit: "bonjour". C\'est sympa!' -## Concaténation +Concaténation +------------- -```python ->>> name = "John" ->>> message = "Bonjour " + name + " !" ->>> message -"Bonjour John !" -``` +.. code-block:: python + >>> name = "John" + >>> message = "Bonjour " + name + " !" + >>> message + "Bonjour John !" diff --git a/cours/source/03-variables-et-types/03-types.rst b/cours/source/03-variables-et-types/03-types.rst index fa599a3..767d7ca 100644 --- a/cours/source/03-variables-et-types/03-types.rst +++ b/cours/source/03-variables-et-types/03-types.rst @@ -1,39 +1,36 @@ -+++ -title = "Types" -weight = 8 -+++ +Types +===== -# Types +.. code-block:: python -```python ->>> a = 42 ->>> message = "La réponse est: " + a -TypeError: can only concatenate str (not "int") to str -``` + >>> a = 42 + >>> message = "La réponse est: " + a + TypeError: can only concatenate str (not "int") to str *Notre premier message d'erreur !* On ne mélange pas les torchons et les serviettes! -## Conversions +Conversions +----------- -### Entier vers string +Entier vers string +++++++++++++++++++ -```python ->>> a = 42 ->>> message = "La réponse est: " + str(a) ->>> message -'La réponse est 42' -``` +.. code-block:: python + >>> a = 42 + >>> message = "La réponse est: " + str(a) + >>> message + 'La réponse est 42' -### String vers entier +String vers entier +++++++++++++++++++ -```python ->>> answer = int("42") ->>> answer -42 -``` +.. code-block:: python -Notez les parenthèses autour des valeurs. + >>> answer = int("42") + >>> answer + 42 +Notez les parenthèses autour des valeurs. diff --git a/cours/source/03-variables-et-types/04-booléens.rst b/cours/source/03-variables-et-types/04-booléens.rst index d28af7b..e6d31b8 100644 --- a/cours/source/03-variables-et-types/04-booléens.rst +++ b/cours/source/03-variables-et-types/04-booléens.rst @@ -1,43 +1,40 @@ -+++ -title = "Booléens et conditions" -weight = 9 -+++ +Booléens et conditions +====================== -# Booléens et conditions - -## True et False +True et False +-------------- En Python ce sont des mots-clés et les valeurs sont en majuscules! -## Assignation +Assignation +----------- On peut assigner des variables aux valeurs True et False -``` ->>> la_terre_est_plate = False -... ->>> python_c_est_genial = True -``` +.. code-block:: python + + la_terre_est_plate = False + python_c_est_genial = True ## Comparaisons -``` ->>> a = 2 ->>> b = 3 ->>> a > b -False -``` +.. code-block:: python -``` ->>> 2 + 2 == 4 -True -``` + >>> a = 2 + >>> b = 3 + >>> a > b + False + +.. code-block:: python + + >>> 2 + 2 == 4 + True -Note: `==` pour la comparaison, `=` pour l'assignation +Note: ``==`` pour la comparaison, ``=`` pour l'assignation ``` diff --git a/cours/source/03-variables-et-types/index.rst b/cours/source/03-variables-et-types/index.rst index 72b5923..41fb8ea 100644 --- a/cours/source/03-variables-et-types/index.rst +++ b/cours/source/03-variables-et-types/index.rst @@ -1,4 +1,10 @@ -+++ -title = "Chapitre 3 - Variables et types" -weight = 3 -+++ +Chapitre 3 - Variables et types +================================ + +.. toctree:: + :maxdepth: 1 + + 01-variables + 02-chaînes-de-caractères + 03-types + 04-booléens diff --git a/cours/source/04-code-source/01-code-source.rst b/cours/source/04-code-source/01-code-source.rst deleted file mode 100644 index 1b9dd88..0000000 --- a/cours/source/04-code-source/01-code-source.rst +++ /dev/null @@ -1,107 +0,0 @@ -+++ -title = "Code source" -weight = 10 -+++ - -# Code source - -## Non persistance des variables - -```python -$ python3 ->>> a = 2 ->>> quit() -``` - -```python -$ python3 ->>> a -Traceback (most recent call last): - File "", line 1, in -NameError: name 'a' is not defined -``` - - -## Du code dans un fichier - -Aussi appelé: "code source", ou "source". - -L'essence du logiciel libre :) - - - -## Installation d'un éditeur de texte simple - -* Linux; `gedit`, `kate`, ... -* macOS: `CotEditor` -* Windows: `Notepad++` - -J'insiste sur **simple**. Inutile d'installer un IDE pour le moment. - - -## Configuration - -* Police de caractères à chasse fixe -* Indentation de *4 espaces* -* Remplacer tabulation par des espaces -* Activer la coloration syntaxique - - - -## Notre premier fichier source - - -Insérez le code suivant dans votre éditeur de texte - -```python -# Affiche un message -print("Bonjour, monde") -``` - - -Sauvegardez dans un fichier `bonjour.py` dans `Documents/e2l/python` par exemple - -# Démonstration avec `kate` - - -// TODO: conseiller un éditeur par plateforme - -C'est l'éditeur que nous utiliserons pour nos ateliers. - -* Pour l'environement KDE, mais ça marche bien sous Gnome aussi -* Coloration syntaxique -* Auto-complétion - - - -## Lancer du code en ligne de commande - - -```text -cd Documents/e2l/python/ -python3 bonjour.py -Bonjour, monde -``` - -* Les lignes commençant par dièse (`#`) ont été ignorées - ce sont des *commentaires*. -* `print()` affiche la valeur, comme dans le REPL. - - -## Note importante - -Vous avez juste besoin: - -* d'un éditeur de texte -* de Python3 installé -* d'une ligne de commande - -Pas la peine d'installer quoique ce soit de plus pour le moment - - -// TODO: dupliqué? - -1. *Recopiez* le code affiché dans votre éditeur, à la suite du code - déjà écrit -2. Lancez le code depuis la ligne de commande -3. Réparez les erreurs éventuelles -4. Recommencez diff --git a/cours/source/04-code-source/index.rst b/cours/source/04-code-source/index.rst index bdb1f50..0c4f642 100644 --- a/cours/source/04-code-source/index.rst +++ b/cours/source/04-code-source/index.rst @@ -1,4 +1,98 @@ -+++ -title = "Chapitre 4 - code source" -weight = 4 -+++ +Chapitre 4 - code source +======================== + +Non persistance des variables +------------------------------ + +.. code-block:: console + + $ python3 + >>> a = 2 + >>> quit() + ``` + +.. code-block:: console + + $ python3 + >>> a + Traceback (most recent call last): + File "", line 1, in + NameError: name 'a' is not defined + + +Du code dans un fichier +----------------------- + +Aussi appelé: "code source", ou "source". + +L'essence du logiciel libre :) + + + +Installation d'un éditeur de texte simple +------------------------------------------ + +* Linux; ``gedit``, ``kate``, ... +* macOS: ``CotEditor`` +* Windows: ``Notepad++`` + +J'insiste sur **simple**. Inutile d'installer un IDE pour le moment. + + +Configuration +------------- + +* Police de caractères à chasse fixe +* Indentation de *4 espaces* +* Remplacer tabulation par des espaces +* Activer la coloration syntaxique + + + +Notre premier fichier source +----------------------------- + + +Insérez le code suivant dans votre éditeur de texte + +.. code-block:: python + + # Affiche un message + print("Bonjour, monde") + + +Sauvegardez dans un fichier `bonjour.py` dans `Documents/e2l/python` par exemple + + +Lancer du code en ligne de commande +----------------------------------- + + +.. code-block:: console + + cd Documents/e2l/python/ + python3 bonjour.py + Bonjour, monde + +* Les lignes commençant par dièse (``#``) ont été ignorées - ce sont des *commentaires*. +* ``print()`` affiche la valeur, comme dans le REPL. + + +Note importante +--------------- + +Vous avez juste besoin: + +* d'un éditeur de texte +* de Python3 installé +* d'une ligne de commande + +Pas la peine d'installer quoique ce soit de plus pour le moment + + +1. *Recopiez* le code affiché dans votre éditeur, à la suite du code + déjà écrit +2. Lancez le code depuis la ligne de commande +3. Réparez les erreurs éventuelles +4. Recommencez + diff --git a/cours/source/05-flot-de-controle/01-flot-de-contrôle.rst b/cours/source/05-flot-de-controle/01-flot-de-contrôle.rst index 3cb84f7..f3d9655 100644 --- a/cours/source/05-flot-de-controle/01-flot-de-contrôle.rst +++ b/cours/source/05-flot-de-controle/01-flot-de-contrôle.rst @@ -1,22 +1,19 @@ -+++ -title = "Flôt de contrôle" -weight = 11 -+++ - -# Flot de contrôle +Flot de contrôle +================ L'essence de la programmation! -## if +if +-- -```python -a = 3 -b = 4 -if a == b: - print("a et b sont égaux") -print("on continue") -``` +.. code-block:: python + + a = 3 + b = 4 + if a == b: + print("a et b sont égaux") + print("on continue") Notes: @@ -26,17 +23,16 @@ Notes: * si la condition n'est pas vraie, rien ne se passe Notez qu'on peut mettre uniquement une variable ou une valeur -après le if. Ceci ne fonctionne pas: +après le if. Ceci ne fonctionne pas:: -```python -if a = 3: - print("a égale 3") -``` + if a = 3: + print("a égale 3") et fait une erreur de syntaxe -## if / else +if / else +--------- ```python a = 3 @@ -48,7 +44,8 @@ else: ``` -## if / elif +if / elif +-------- ```python if age < 10: @@ -65,7 +62,8 @@ On peut mettre autont de `elif` qu'on veut! Le derier `else` s'éxécute en dernier -## while +while +----- Répéter tant qu'une condition est vraie @@ -83,7 +81,8 @@ while i < 3: ``` -## Notre première boucle infinie +Notre première boucle infinie +----------------------------- ```python while True: @@ -93,7 +92,8 @@ while True: CTRL-C pour interrompre -## Combiner while et if +Combiner while et if +-------------------- On peut "sortir" de la boucle `while` avec `break` diff --git a/cours/source/05-flot-de-controle/02-exercice.rst b/cours/source/05-flot-de-controle/02-exercice.rst index 618ef84..0d9b801 100644 --- a/cours/source/05-flot-de-controle/02-exercice.rst +++ b/cours/source/05-flot-de-controle/02-exercice.rst @@ -1,13 +1,10 @@ -+++ -title = "Exercice" -weight = 12 -+++ - -# Exercice +Exercice +======== // TODO: explication des exercises -## Lire une entrée utilisateur +Lire une entrée utilisateur +---------------------------- * `input()` (encore des parenthèses ...) @@ -15,24 +12,26 @@ weight = 12 * lit ce que l'utilisateur tape jusqu'à ce qu'il tape "entrée". * renvoie une string -## Le jeu +Le jeu +------ On fait deviner un nombre à l'utilisateur, en affichant 'trop grand', 'trop petit' jusqu'à ce qu'il trouve la valeur exacte. -## Squelette +Squelette +-------- // TODO: * explication du Squelette * pas de solution! -```python -# faites moi confiance, les deux lignes ci-dessous -# permettent de tirer un nombre au hasard entre 0 et 100 -import random -nombre = random.randint(0, 100) +.. code-block:: python + + # faites moi confiance, les deux lignes ci-dessous + # permettent de tirer un nombre au hasard entre 0 et 100 + import random + nombre = random.randint(0, 100) -print("devine le nombre auquel je pense") + print("devine le nombre auquel je pense") -# votre code ici -``` + # votre code ici diff --git a/cours/source/05-flot-de-controle/index.rst b/cours/source/05-flot-de-controle/index.rst index f411f0c..218e6b1 100644 --- a/cours/source/05-flot-de-controle/index.rst +++ b/cours/source/05-flot-de-controle/index.rst @@ -1,4 +1,8 @@ -+++ -title = "Chapitre 5 - Flot de contrôle" -weight = 5 -+++ +Chapitre 5 - Flot de contrôle +============================= + +.. toctree:: + :maxdepth: 1 + + 01-flot-de-contrôle + 02-exercice diff --git a/cours/source/06-fonctions/01-functions.rst b/cours/source/06-fonctions/01-functions.rst index 3239ee6..11fb906 100644 --- a/cours/source/06-fonctions/01-functions.rst +++ b/cours/source/06-fonctions/01-functions.rst @@ -1,30 +1,26 @@ -+++ -title = "Fonctions" -weight = 1 -+++ +Fonctions +========= -# Fonctions +Fonction sans argument +--------------------- -## Fonction sans argument +Définition:: + + def dire_bonjour(): + print("Bonjour") -Définition: -```python -def dire_bonjour(): - print("Bonjour") -``` * avec `def` * avec un `:` à la fin et un _bloc indenté_ (appelé le "corps") -Appel: -``` ->>> dire_bonjour() -Bonjour -``` +Appel:: + >>> dire_bonjour() + Bonjour * avec le nom de la fonction et des parenthèses -## Le pouvoir des fonctions +Le pouvoir des fonctions +------------------------ Ici on vient de créer une nouvelle fonctionnalité à Python. Avant qu'on définisse la fonction @@ -37,39 +33,33 @@ c'est une technique extrêmement utile en programmation. -## Fonction avec un argument - -Définition: avec l'argument à l'intérieur des parenthèses +Fonction avec un argument -```python -def dire_bonjour(prénom): - print("Bonjour " + prénom) -``` +Définition: avec l'argument à l'intérieur des parenthèses:: -Appel: en passant une variable ou une valeur dans les parenthèses + def dire_bonjour(prénom): + print("Bonjour " + prénom) +Appel: en passant une variable ou une valeur dans les parenthèses:: -```python ->>> dire_bonjour("Germaine") -Bonjour Germaine + >>> dire_bonjour("Germaine") + Bonjour Germaine ->>> prénom_de_charlotte = "Charlotte" ->>> dire_bonjour(prénom_de_charlotte) -Bonjour Charlotte -``` + >>> prénom_de_charlotte = "Charlotte" + >>> dire_bonjour(prénom_de_charlotte) + Bonjour Charlotte -## Exécution d'une fonction +Exécution d'une fonction +------------------------ C'est exatement comme si on assignait les arguments de la fonction avant d'éxécuter le code -dans le corps +dans le corps:: -```python -# Ceci: -dire_bonjour("Dimitri") + # Ceci: + dire_bonjour("Dimitri") -# Est équivalent à cela: -prénom_de_dimitri = "Dimitri" -print("Bonjour " + prénom_de_dimitri) + # Est équivalent à cela: + prénom_de_dimitri = "Dimitri" + print("Bonjour " + prénom_de_dimitri) -# Lui-même équivalent à: -print("Bonjour " + "Dimitri") -``` + # Lui-même équivalent à: + print("Bonjour " + "Dimitri") diff --git a/cours/source/06-fonctions/02-portée-des-variables.rst b/cours/source/06-fonctions/02-portée-des-variables.rst index fe883e2..4e53c27 100644 --- a/cours/source/06-fonctions/02-portée-des-variables.rst +++ b/cours/source/06-fonctions/02-portée-des-variables.rst @@ -1,39 +1,29 @@ -+++ -title = "Portée des variables" -weight = 2 -+++ +Portée des variables +==================== -# Portée des variables +Les arguments d'une fonction n'existent que dans le corps de celle-ci:: -Les arguments d'une fonction n'existent que dans le corps de celle-ci + def dire_bonjour(prénom): + print("Bonjour " + prénom) -```python -def dire_bonjour(prénom): - print("Bonjour " + prénom) + dire_bonjour("Dimitri") # Ok + print(prénom) # Erreur -dire_bonjour("Dimitri") # Ok -print(prénom) # Erreur -``` +Les variables en dehors des fonctions sont disponibles partout:: -Les variables en dehors des fonctions sont disponibles partout: + salutation = "Bonjour " -```python -salutation = "Bonjour " + def dire_bonjour(prénom): + print(salutation + prénom) -def dire_bonjour(prénom): - print(salutation + prénom) + dire_bonjour("Dimitri") -dire_bonjour("Dimitri") -``` +Une variable peut avoir en "cacher" une autre si elle a une portée différente:: -Une variable peut avoir en "cacher" une autre si elle a une portée différente + def dire_bonjour(prénom): + print("Bonjour " + prénom) # portée: uniquement dans + # le corps dire_bonjour -```python -def dire_bonjour(prénom): - print("Bonjour " + prénom) # portée: uniquement dans - # le corps dire_bonjour - -prénom = "Dimitri" # portée: dans tout le programme -dire_bonjour(prénom) # Ok -``` + prénom = "Dimitri" # portée: dans tout le programme + dire_bonjour(prénom) # Ok diff --git a/cours/source/06-fonctions/03-plusieurs-arguments.rst b/cours/source/06-fonctions/03-plusieurs-arguments.rst index 33b0439..2dca4a2 100644 --- a/cours/source/06-fonctions/03-plusieurs-arguments.rst +++ b/cours/source/06-fonctions/03-plusieurs-arguments.rst @@ -1,43 +1,29 @@ -+++ -title = "Fonctions à plusieurs arguments" -weight = 2 -+++ - -# Fonctions à plusieurs arguments +Fonctions à plusieurs arguments +=============================== On peut mettre autant d'arguments qu'on veut, séparés -par des virgules: -```python -def afficher_addition(x, y): - résultat = x + y - print(résultat) -``` - -```python ->>> a = 4 ->>> b = 5 ->>> afficher_addition(a, b) -9 -``` - -## Arguments nommés +par des virgules:: -En Python, on peut aussi utiliser le *nom* des arguments au lieu de -leur position: + def soustraction(x, y): + résultat = x - y + return résultat -```python -def dire_bonjour(prénom): - print("Bonjour " + prénom) -``` + résultat = soustraction(5, 4) + print(résultat) + # affiche: 1 -```python ->>> dire_bonjour(prénom="Gertrude") -Bonjour Gertrude +Arguments nommés +---------------- ->>> afficher_addition(y=3, x=4) -7 -``` +En Python, on peut aussi utiliser le *nom* des arguments au lieu de +leur position:: + def dire_bonjour(prénom): + print("Bonjour " + prénom) -// TODO: soustraction + dire_bonjour(prénom="Gertrude") + # Affiche: Bonjour Gertrude + résultat = soustraction(y=4, x=5) + print(résultat) + # affiche: 1 diff --git a/cours/source/06-fonctions/04 b/cours/source/06-fonctions/04 deleted file mode 100644 index 8b13789..0000000 --- a/cours/source/06-fonctions/04 +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cours/source/06-fonctions/04-par-défaut.rst b/cours/source/06-fonctions/04-par-défaut.rst index e07195e..77b5ee6 100644 --- a/cours/source/06-fonctions/04-par-défaut.rst +++ b/cours/source/06-fonctions/04-par-défaut.rst @@ -1,28 +1,19 @@ -+++ -title = "Arguments par défaut" -weight = 4 -+++ +Arguments par défaut +==================== -# Arguments par défaut +On peut aussi mettre des valeurs par défaut:: -On peut aussi mettre des valeurs par défaut: + def dire_bonjour(prénom, enthousiaste=False): + message = "Bonjour " + prénom + if enthousiaste: + message += "!" + print(message) -Définition: -```python -def dire_bonjour(prénom, enthousiaste=False): - message = "Bonjour " + prénom - if enthousiaste: - message += "!" - print(message) -``` - -Appel: -```python ->>> dire_bonjour("Thomas", enthousiaste=True) -Bonjour Thomas! ->>> dire_bonjour("Thomas", enthousiaste=False) -Bonjour Thomas ->>> dire_bonjour("Thomas") -Bonjour Thomas -``` +Appel:: + >>> dire_bonjour("Thomas", enthousiaste=True) + Bonjour Thomas! + >>> dire_bonjour("Thomas", enthousiaste=False) + Bonjour Thomas + >>> dire_bonjour("Thomas") + Bonjour Thomas diff --git a/cours/source/06-fonctions/05-fonctions-natives.rst b/cours/source/06-fonctions/05-fonctions-natives.rst index 1c32c53..536a243 100644 --- a/cours/source/06-fonctions/05-fonctions-natives.rst +++ b/cours/source/06-fonctions/05-fonctions-natives.rst @@ -1,46 +1,36 @@ -+++ -title = "Fonctions natives" -weight = 5 -+++ - -# Fonctions natives +Fonctions natives +================= Fonctions qui sont toujours présentes dans l'interpréteur. On en a déjà vu quelques unes: -* `print`, `input`: écrire et lire sur la ligne de commande -* `str`, `int`: convertir des entiers en strings et vice-versa +* ``print``, ``input``: écrire et lire sur la ligne de commande +* ``str``, ``int``: convertir des entiers en strings et vice-versa Il y en a tout un tas! La liste ici: https://docs.python.org/fr/3/library/functions.html#built-in-funcs -## Retour sur print +Retour sur print +---------------- -On peut passer autant d'arguments qu'on veut à `print` et: +On peut passer autant d'arguments qu'on veut à ``print`` et: * Il les sépare par des espaces -* Ajoute un retour à la ligne à la fin: - -```python ->>> prénom = "Charlotte" -print("Bonjour", pŕenom) -Bonjour Charlotte -``` +* Ajoute un retour à la ligne à la fin:: -On peut demander à `print` de changer son séparateur: + >>> prénom = "Charlotte" + print("Bonjour", pŕenom) + Bonjour Charlotte -```python ->>> a = "chauve" ->>> b = "souris" ->>> print(a, b, sep="-") -chauve-souris -``` +On peut demander à `print` de changer son séparateur:: -Ou de changer le caractère de fin: -```python ->>> print("Ceci tient", end="") ->>> print("sur une seule ligne") -Ceci tient sur une seule ligne -``` + >>> a = "chauve" + >>> b = "souris" + >>> print(a, b, sep="-") + chauve-souris +Ou de changer le caractère de fin:: + >>> print("Ceci tient", end="") + >>> print("sur une seule ligne") + Ceci tient sur une seule ligne diff --git a/cours/source/06-fonctions/06-return.rst b/cours/source/06-fonctions/06-return.rst index 1c65cdf..682008f 100644 --- a/cours/source/06-fonctions/06-return.rst +++ b/cours/source/06-fonctions/06-return.rst @@ -1,43 +1,34 @@ -+++ -title = "return" -weight = 6 -+++ - -# Valeur de retour d'une fonction - -Définition avec le mot `return` - -```python -def additionner(x, y): - return x + y -``` - -Récupérer la valeur de retour -```python ->>> a = 3 ->>> b = 4 ->>> c = additionner(a, b) # encore une assignation ->>> c -7 -``` - -# Sortir d'une fonction avec return - -`return` interrompt également l'éxécution du -corps de la fonction: - -```python -def dire_bonjour(prénom, première_fois=False): - print("Bonjour", prénom) - if not première_fois: - return - print("Heureux de faire votre connaissance") -``` - -```python ->>> dire_bonjour("Dimitri", première_fois=True) -Bonjour Dimitri -Heureux de faire votre connaissance ->>> dire_bonjour("Dimitri", première_fois=False) -Bonjour Dimitri -``` +Valeur de retour d'une fonction +================================= + +Définition avec le mot ``return``:: + + def additionner(x, y): + return x + y + +Récupérer la valeur de retour:: + + a = 3 + b = 4 + c = additionner(a, b) # encore une assignation + print(c) + # Affche: 7 + +Sortir d'une fonction avec return +--------------------------------- + +``return`` interrompt également l'éxécution du +corps de la fonction:: + + def dire_bonjour(prénom, première_fois=False): + print("Bonjour", prénom) + if not première_fois: + return + print("Heureux de faire votre connaissance") + + >>> dire_bonjour("Dimitri", première_fois=True) + Bonjour Dimitri + Heureux de faire votre connaissance + + >>> dire_bonjour("Dimitri", première_fois=False) + Bonjour Dimitri diff --git a/cours/source/06-fonctions/index.rst b/cours/source/06-fonctions/index.rst index 659c5d0..bf83627 100644 --- a/cours/source/06-fonctions/index.rst +++ b/cours/source/06-fonctions/index.rst @@ -1,4 +1,12 @@ -+++ -title = "Chapitre 6 - Fonctions" -weight = 6 -+++ +Chapitre 6 - Fonctions +====================== + +.. toctree:: + :maxdepth: 1 + + 01-functions.rst + 02-portée-des-variables.rst + 03-plusieurs-arguments.rst + 04-par-défaut.rst + 05-fonctions-natives.rst + 06-return.rst diff --git a/cours/source/07-listes/01-listes.rst b/cours/source/07-listes/01-listes.rst deleted file mode 100644 index cef92d3..0000000 --- a/cours/source/07-listes/01-listes.rst +++ /dev/null @@ -1,164 +0,0 @@ -+++ -title = "Listes" -weight = 1 -+++ - -# Listes - -// TODO: split in pages - -## Définition - -Une liste est une _suite ordonée_ d'éléments. - -## Créer une liste - -Avec `[]`, et les élements séparés par des virgules: - -```python -liste_vide = [] -trois_entiers = [1, 2, 3] -``` - - -## Listes hétérogènes - -On peut mettre des types différents dans la même liste - -```python -ma_liste = [True, 2, "trois"] -``` - -On peut aussi mettre des listes dans des listes: - -```python -liste_de_listes = [[1, 2], ["Germaine", "Gertrude"]] -``` - -## Connaître la taille d'une liste - -Avec `len()` - encore une fonction native - -```python ->>> liste_vide = [] ->>> len(liste_vide) -0 ->>> trois_entiers = [1, 2, 3] ->>> len(trois_entiers) -3 -``` - -## Concaténation de listes - -Avec `+` - -```python ->>> prénoms = ["Alice", "Bob"] ->>> prénoms += ["Charlie", "Eve"] ->>> prénoms -['Alice', 'Bob', "Charlie", 'Eve'] -``` - -On ne peut concaténer des listes que avec d'autres listes: - -```python ->>> scores = [1, 2, 3] ->>> scores += 4 # TypeError ->>> scores += [4] # OK -``` - -## Test d'appartenance - -Avec `in`: - -```python ->>> prénoms = ["Alice", "Bob"] ->>> "Alice" in prénoms -True -``` - -```python ->>> prénoms = ["Alice", "Bob"] ->>> "Charlie" in prénoms -False -``` - -## Itérer sur les élements d'une liste - -Avec `for ... in` - -```python -prénoms = ["Alice", "Bob", "Charlie"] -for prénom in prénoms: - # La variable 'prénom" est assignée à chaque - # élément de la liste - print("Bonjour", prénom) - -Bonjour Alice -Bonjour Bob -Bonjour Charlie -``` - -## Indéxer une liste - -* Avec `[]` et un entier - -* Les index valides vont de 0 à `n-1` où `n` est la -taille de la liste. - -```python ->>> fruits = ["pomme", "orange", "poire"] ->>> fruits[0] -"pomme" ->>> fruits[1] -"orange" ->>> list[2] -"poire" ->>> fruits[3] # IndexError -``` - -## Modifier une liste - -Encore une assignation: - -```python ->>> fruits = ["pomme", "orange", "poire"] ->>> fruits[0] = "abricot" ->>> fruits -["abricot", "orange", "poire"] -``` - -## Les strings sont aussi des listes (presque) - -On peut itérer sur les caractères d'une string: - -```python -for c in "vache": - print(c) -v -a -c -h -e -``` - -On peut tester si un caractère est présent: - -```python ->>> "e" in "vache" -True ->>> "x" in "vache" -False -``` - - -Mais on neut peut pas modifier une string - -```python ->>> prénom = "Charlotte" ->>> prénom[0] -"C" ->>> prénom[3] -"r" ->>> prénom[0] = "X" # TypeError -``` diff --git a/cours/source/07-listes/index.rst b/cours/source/07-listes/index.rst index e8b0815..6b61de2 100644 --- a/cours/source/07-listes/index.rst +++ b/cours/source/07-listes/index.rst @@ -1,4 +1,150 @@ -+++ -title = "Chapitre 7 - Listes" -weight = 7 -+++ +Chapitre 7 - Listes +=================== + +// TODO: split in pages + +Définition +---------- + +Une liste est une _suite ordonée_ d'éléments. + +Créer une liste +--------------- + +Avec des crochets: ``[``, ``]``, et les élements séparés par des virgules:: + + liste_vide = [] + trois_entiers = [1, 2, 3] + + +Listes hétérogènes +------------------ + +On peut mettre des types différents dans la même liste:: + + ma_liste = [True, 2, "trois"] + +On peut aussi mettre des listes dans des listes:: + + liste_de_listes = [[1, 2], ["Germaine", "Gertrude"]] + +Connaître la taille d'une liste +------------------------------- + +Avec ``len()`` - encore une fonction native:: + + >>> liste_vide = [] + >>> len(liste_vide) + 0 + >>> trois_entiers = [1, 2, 3] + >>> len(trois_entiers) + 3 + +Concaténation de listes +----------------------- + +Avec ``+``:: + + >>> prénoms = ["Alice", "Bob"] + >>> prénoms += ["Charlie", "Eve"] + >>> prénoms + ['Alice', 'Bob', "Charlie", 'Eve'] + +On ne peut concaténer des listes que avec d'autres listes:: + + >>> scores = [1, 2, 3] + >>> scores += 4 # TypeError + >>> scores += [4] # OK + +Test d'appartenance +------------------- + +Avec ``in``:: + + >>> prénoms = ["Alice", "Bob"] + >>> "Alice" in prénoms + True + + >>> prénoms = ["Alice", "Bob"] + >>> "Charlie" in prénoms + False + +Itérer sur les élements d'une liste +------------------------------------ + +Avec ``for ... in``:: + + prénoms = ["Alice", "Bob", "Charlie"] + for prénom in prénoms: + # La variable 'prénom" est assignée à chaque + # élément de la liste + print("Bonjour", prénom) + + Bonjour Alice + Bonjour Bob + Bonjour Charlie + +## Indéxer une liste + +* Avec `[]` et un entier + +* Les index valides vont de 0 à `n-1` où `n` est la +taille de la liste. + +```python +>>> fruits = ["pomme", "orange", "poire"] +>>> fruits[0] +"pomme" +>>> fruits[1] +"orange" +>>> list[2] +"poire" +>>> fruits[3] # IndexError +``` + +## Modifier une liste + +Encore une assignation: + +```python +>>> fruits = ["pomme", "orange", "poire"] +>>> fruits[0] = "abricot" +>>> fruits +["abricot", "orange", "poire"] +``` + +## Les strings sont aussi des listes (presque) + +On peut itérer sur les caractères d'une string: + +```python +for c in "vache": + print(c) +v +a +c +h +e +``` + +On peut tester si un caractère est présent: + +```python +>>> "e" in "vache" +True +>>> "x" in "vache" +False +``` + + +Mais on neut peut pas modifier une string + +```python +>>> prénom = "Charlotte" +>>> prénom[0] +"C" +>>> prénom[3] +"r" +>>> prénom[0] = "X" # TypeError +``` + diff --git a/cours/source/08-none-et-pass/01-none.rst b/cours/source/08-none-et-pass/01-none.rst index b185290..db96ae2 100644 --- a/cours/source/08-none-et-pass/01-none.rst +++ b/cours/source/08-none-et-pass/01-none.rst @@ -1,98 +1,80 @@ -+++ -title = "None" -weight = 1 -+++ +None +==== -# None +Définition +----------- -## Définition +``None`` est une "valeur magique" natif en python. il est toujours présent, et il est unique. -`None` est une "valeur magique" natif en Python. Il est toujours présent, et il est unique. +Un peu comme ``True`` et ``False`` qui sont deux valeurs qui servent à représenter tous les booléens. -Un peu comme `True` et `False` qui sont deux valeurs qui servent à représenter tous les booléens. +Représenter l'absence +---------------------- -## Représenter l'absence +L'interpréteur intéractif n'affiche rien quand la valeur est None:: -L'interpréteur intéractif n'affiche rien quand la valeur est None + >>> a = 42 + >>> a + 42 + >>> b = None + >>> b -```python ->>> a = 42 ->>> a -42 ->>> b = None ->>> b -``` - -## Retourner None +Retourner None +---------------- En réalité, *toutes* les fonctions pythons retournent *quelque chose*, même quand -elle ne contiennent pas le mot-clé `return`. - -```python -def ne_renvoie_rien(): - print("je ne fais qu'afficher quelque chose") -``` - -```python ->>> resultat = ne_renvoie_rien() -"je ne fais qu'afficher quelque chose" ->>> resultat -``` - -## Opérations avec None - -La plupart des fonctions que nous avons vues échouent si on leur passe None -en argument: - -```python ->>> len(None) -TypeError: object of type 'NoneType' has no len() ->>> None < 3 -TypeError: '<' not supported between instances of - 'NoneType' and 'int' ->>> int(None) -TypeError: int() argument must be a string, - a bytes-like object or a number, - not 'NoneType' ->>> str(None) -'None' -``` - -## Example d'utilisation: - -```python -def trouve_dans_liste(valeur, liste): - for element in liste: - if element == valeur: - return element - return None -``` - -```python ->>> trouve_dans_liste(2, [1, 2, 3]) -2 ->>> trouve_dans_liste(False, [True, False]) -False ->>> trouve_dans_liste(1, [3, 4]) -``` - - -```python -def trouve_dans_liste(liste, valeur): - for element in liste: - if element == valeur: - return element -``` - -None est Falsy, et on peut vérifier si une variable vaut None avec `is None` - -```python -# hypothèse: `ma_valeur` n'est pas None -mon_element = trouve_dans_liste(ma_valeur, ma_liste) -if mon_element is None: - print("élément absent de la liste") -if not mon_element: - # Peut-être que l'élément n'était pas dans la liste, - # ou peut-être y était-il, mais avec une valeur falsy - ... -``` +elle ne contiennent pas le mot-clé ``return``.:: + + def ne_renvoie_rien(): + print("je ne fais qu'afficher quelque chose") + + >>> resultat = ne_renvoie_rien() + "je ne fais qu'afficher quelque chose" + >>> resultat + +Opérations avec None +--------------------- + +La plupart des fonctions que nous avons vues échouent si on leur passe ``None`` +en argument:: + + >>> len(None) + TypeError: object of type 'NoneType' has no len() + >>> None < 3 + TypeError: '<' not supported between instances of + 'NoneType' and 'int' + >>> int(None) + TypeError: int() argument must be a string, + a bytes-like object or a number, + not 'NoneType' + >>> str(None) + 'None' + +Example d'utilisation +---------------------- + +.. code-block:: python + + def trouve_dans_liste(valeur, liste): + for element in liste: + if element == valeur: + return element + return None + + >>> trouve_dans_liste(2, [1, 2, 3]) + 2 + >>> trouve_dans_liste(False, [True, False]) + False + >>> trouve_dans_liste(1, [3, 4]) + + +None est Falsy, et on peut vérifier si une variable vaut ``None`` avec ``is None``:: + + # hypothèse: `ma_valeur` n'est pas None + mon_element = trouve_dans_liste(ma_valeur, ma_liste) + if mon_element is None: + print("élément absent de la liste") + if not mon_element: + # Peut-être que l'élément n'était pas dans la liste, + # ou peut-être y était-il, mais avec une valeur falsy + ... diff --git a/cours/source/08-none-et-pass/02-pass.rst b/cours/source/08-none-et-pass/02-pass.rst index 8a396f8..4624730 100644 --- a/cours/source/08-none-et-pass/02-pass.rst +++ b/cours/source/08-none-et-pass/02-pass.rst @@ -1,34 +1,21 @@ -+++ -title = "pass" -weight = 2 -+++ - -# pass +pass +==== En Python, à cause de l'organisation en blocs indentés, on ne peut pas vraiment avoir de blocs vides. Mais parfois, on a besoin d'un bloc qui ne fasse rien. Dans ce cas, on peut utiliser le mot-clé `pass`, par exemple -après un if: +après un if:: -```python -une_condition = False -if une_condition: - pass -else: - print("une_condition n'est pas vraie") -``` + une_condition = False + if une_condition: + pass + else: + print("une_condition n'est pas vraie") On peut aussi - et c'est l'usage le plus courant - utiliser `pass` pour -définir une fonction qui ne fait rien: - -```python -def ne_fait_rien(): - pass -``` +définir une fonction qui ne fait rien:: -```python ->>> ne_fait_rien() - -``` + def ne_fait_rien(): + pass diff --git a/cours/source/08-none-et-pass/index.rst b/cours/source/08-none-et-pass/index.rst index f91d7c7..fd9a00d 100644 --- a/cours/source/08-none-et-pass/index.rst +++ b/cours/source/08-none-et-pass/index.rst @@ -1,4 +1,8 @@ -+++ -title = "Chapitre 8 - None et pass" -weight = 8 -+++ +Chapitre 8 - None et pass +========================= + +.. toctree:: + :maxdepth: 1 + + 01-none + 02-pass diff --git a/cours/source/09-dictionnaires/01-dictionnaires.rst b/cours/source/09-dictionnaires/01-dictionnaires.rst deleted file mode 100644 index 91c46ae..0000000 --- a/cours/source/09-dictionnaires/01-dictionnaires.rst +++ /dev/null @@ -1,205 +0,0 @@ -+++ -title = "Dictionnaires" -weight = 1 -+++ - -# Dictionnaires - -## Définition - -Un dictionaire est une _association_ entre des clés et des valeurs. - -* Les clés sont uniques -* Les valeurs sont arbitraires - -## Création de dictionnaires - -```python -# dictionaire vide ->>> {} - -# une clé, une valeur ->>> {"a": 42} - -# deux clés, deux valeurs ->>> {"a": 42, "b": 53} - -# les clés sont uniques: ->>> {"a": 42, "a": 53} -{"a": 53} -``` - -Note: tous les dictionnaires sont truthy, sauf les dictionnaires vides. - -## Accès aux valeurs - -Avec `[]`, comme pour les listes, mais avec une *clé* à la place d'un *index*. - -```python ->>> scores = {"john": 10, "bob": 42} ->>> scores["john"] -10 ->>> scores["bob"] -42 ->>> scores["charlie"] -KeyError -``` - -## Test d'appartenance - -Avec `in`, comme le listes: - -```python ->>> scores = {"john": 10, "bob": 42} ->>> "charlie" in scores -False -``` - -## Modifier la valeur d'une clé - -Comme pour les listes: on assigne la nouvelle variable: - -```python ->>> scores = {"john": 10, "bob": 42} ->>> scores["john"] = 20 ->>> scores -{"john": 20, "bob": 42} -``` - -## Créer une nouvelle clé - -Même méchanisme que pour la modification des clés existantes - -```python ->>> scores = {"john": 10, "bob": 42} ->>> scores["charlie"] = 30 ->>> scores -{"john": 20, "bob": 42, "charlie": 30} -``` - -*rappel*: ceci ne fonctionne pas avec les listes! -```python ->>> ma_liste = ["a", "b"] ->>> ma_liste[1] = "c" # ok -["a", "c"] ->>> ma_liste[3] = "d" -IndexError -``` - -## Itérer sur les clés - -Avec `for ... in ...`, comme pour les listes - -```python -scores = {"john": 10, "bob": 42} -for nom in scores: - # `nom` est assigné à "john" puis "bob" - score_associé_au_nom = scores[nom] - print(nom, score_associé_au_nom) -``` - - -## Détruire une clé - -Avec `del` - un nouveau mot-clé: - -```python ->>> scores = {"john": 10, "bob": 42} ->>> del scores["bob"] ->>> scores -{"john": 10} -``` - -## Détruire un élément d'une liste - -```python - ->>> fruits = ["pomme", "banane", "poire"] ->>> del fruits[1] ->>> fruits -["pomme", "poire"] -``` - -## Détruire une variable - -```python ->>> mon_entier = 42 ->>> mon_entier += 3 ->>> mon_entier -45 ->>> del mon_entier ->>> mon_entier == 45 -NameError: name 'mon_entier' is not defined -``` - -## Détruire une fonction - -On peu aussi supprimer des fonctions: - -```python -def ma_fonction(): - print("bonjour") - - -del ma_fonction ->>> ma_fonction() -NameError: name 'ma_fonction' is not defined -``` - -## Des dictionnaires partout - -Les variables globales d'un programme Python sont dans un dictionnaire, -accessible avec la fonction native `globals()`: - -```python -$ python3 ->>> globals() -{ - ... - '__doc__': None, - '__name__': '__main__', - ... -} -``` - -On reparlera de `__doc__` et `__name__` un autre jour ... - - -```python -$ python3 ->>> a = 42 ->>> globals() -{ - ... - '__doc__': None, - '__name__': '__main__', - ... - 'a': 42 -} -``` - - -```python -$ python3 ->>> a = 42 ->>> del globals()["a"] ->>> a -NameError: name 'a' is not defined -``` - - -On peut accéder aux variables locales d'une fonction avec `locals()` - -```python -def ma_fonction(): - a = 42 - b = 3 - c = a + b - print(locals()) - ->>> ma_fonction() -{'a': 42, 'b': 3, 'c': 45} -``` - -En revanche, il n'est pas conseillé de modifier le dictionaire renvoyé par `locals()` ... - diff --git a/cours/source/09-dictionnaires/index.rst b/cours/source/09-dictionnaires/index.rst index ec5c156..a256d1f 100644 --- a/cours/source/09-dictionnaires/index.rst +++ b/cours/source/09-dictionnaires/index.rst @@ -1,4 +1,176 @@ -+++ -title = "Chapitre 9 - Dictionnaires" -weight = 9 -+++ +Chapitre 9 - Dictionnaires +========================== + +Définition +---------- + +Un dictionaire est une _association_ entre des clés et des valeurs. + +* Les clés sont uniques +* Les valeurs sont arbitraires + +Création de dictionnaires +------------------------- + +Avec des accolades: ``{``, ``}`` :: + + dictionaire_vide = {} + + une_clé_une_valeur = {"a": 42} + + deux_clés_deux_valeurs = {"a": 42, "b": 53} + +Les clés sont uniques:: + + {"a": 42, "a": 53} == {"a": 53} + +Note: tous les dictionnaires sont truthy, sauf les dictionnaires vides. + +Accès aux valeurs +------------------ + +Avec ``[]``, comme pour les listes, mais avec une *clé* à la place d'un *index*:: + + >>> scores = {"john": 10, "bob": 42} + >>> scores["john"] + 10 + >>> scores["bob"] + 42 + >>> scores["charlie"] + KeyError + +Test d'appartenance +--------------------- + +Avec ``in``, comme le listes:: + + >>> scores = {"john": 10, "bob": 42} + >>> "charlie" in scores + False + +Modifier la valeur d'une clé +----------------------------- + +Comme pour les listes, avec une assignation:: + + >>> scores = {"john": 10, "bob": 42} + >>> scores["john"] = 20 + >>> scores + {"john": 20, "bob": 42} + +Créer une nouvelle clé +----------------------- + +Même méchanisme que pour la modification des clés existantes:: + + >>> scores = {"john": 10, "bob": 42} + >>> scores["charlie"] = 30 + >>> scores + {"john": 20, "bob": 42, "charlie": 30} + +*rappel*: ceci ne fonctionne pas avec les listes!:: + >>> ma_liste = ["a", "b"] + >>> ma_liste[1] = "c" # ok + ["a", "c"] + >>> ma_liste[3] = "d" + IndexError + +Itérer sur les clés +------------------- + +Avec ``for ... in ...``, comme pour les listes:: + + scores = {"john": 10, "bob": 42} + for nom in scores: + # `nom` est assigné à "john" puis "bob" + score_associé_au_nom = scores[nom] + print(nom, score_associé_au_nom) + +del +--- + +Détruire une clé ++++++++++++++++++ + +Avec ``del`` - un nouveau mot-clé:: + + >>> scores = {"john": 10, "bob": 42} + >>> del scores["bob"] + >>> scores + {"john": 10} + +Détruire un élément d'une liste +++++++++++++++++++++++++++++++++ + +Aussi avec ``del``:: + + >>> fruits = ["pomme", "banane", "poire"] + >>> del fruits[1] + >>> fruits + ["pomme", "poire"] + +Détruire une variable ++++++++++++++++++++++ + +Encore et toujours ``del``:: + + >>> mon_entier = 42 + >>> mon_entier += 3 + >>> mon_entier + 45 + >>> del mon_entier + >>> mon_entier == 45 + NameError: name 'mon_entier' is not defined + +Des dictionnaires partout +--------------------------- + +Les variables globales d'un programme Python sont dans un dictionnaire, +accessible avec la fonction native `globals()`:: + + $ python3 + >>> globals() + { + ... + '__doc__': None, + '__name__': '__main__', + ... + } + +On reparlera de `__doc__` et `__name__` un autre jour ...:: + + + $ python3 + >>> a = 42 + >>> globals() + { + ... + '__doc__': None, + '__name__': '__main__', + ... + 'a': 42 + } + ``` + +.. code-block:: + + python + $ python3 + >>> a = 42 + >>> del globals()["a"] + >>> a + NameError: name 'a' is not defined + + +On peut accéder aux variables locales d'une fonction avec ``locals()``:: + + def ma_fonction(): + a = 42 + b = 3 + c = a + b + print(locals()) + + >>> ma_fonction() + {'a': 42, 'b': 3, 'c': 45} + +En revanche, il n'est pas conseillé de modifier le dictionaire renvoyé par ``locals()`` ... diff --git a/cours/source/10-tuples/01-tuples.rst b/cours/source/10-tuples/01-tuples.rst deleted file mode 100644 index 58fabea..0000000 --- a/cours/source/10-tuples/01-tuples.rst +++ /dev/null @@ -1,169 +0,0 @@ -+++ -title = "Tuples" -weight = 1 -+++ - -# Tuples - -## Définition - -Un tuple est un ensemble *ordonné* et *immuable* d'éléments. Le nombre, l'ordre et la valeur des éléments sont fixes. - -## Création de tuples - -```python -# Un tuple vide -() - -# Un tuple à un élément -(1,) # notez la virgule - -# Un tuple à deux éléments, aussi appelé couple -(1, 2) -``` - -Sauf pour le tuple vide, c'est la *virgule* qui fait le tuple - -Note: tous les tuples sont truthy, sauf les tuples vides. - -# Tuples hétérogènes - -Comme les listes, les tuples peuvent contenir des éléments de types différents: - -```python -# Un entier et une string -mon_tuple = (42, "bonjour") - -# Un entier et un autre tuple -mon_tuple = (21, (True, "au revoir")) -``` - -## Accès - -Avec `[]` et l'index de l'élément dans le tuple: - -```python -mon_tuple = (42, "bonjour") -mon_tuple[0] -42 -mon_tuple[1] -"bonjour" -``` - -## Modification - -Interdit! - -```python -mon_tuple = (42, "bonjour") -mon_tuple[0] = 44 -TypeError: 'tuple' object does not support item assignment -``` - - -## Test d'appartenance - -Avec `in` - -```python ->>> mon_tuple = (42, 14) ->>> 42 in mon_tuple -True ->>> 14 in mon_tuple -True ->>> 13 in mon_tuple -False -``` - -## Déstructuration - -Créer plusieurs variables en une seule ligne: - -```python ->>> couple = ("Batman", "Robin") ->>> héros, side_kick = couple ->>> héros -'Batman' ->>> side_kick -'Robin' -``` - - -## Quelques erreurs classiques - -```python ->>> héros, side_kick, ennemi = couple -ValueError (3 != 2) - ->>> (héros,) = couple -ValueError (1 != 2) - -# Gare à la virgule: ->>> héros, = couple -ValueError (1 != 2) -``` - -## Pièges - -```python -f(a, b, c) # appelle f() avec trois arguments - -f((a, b, c)) # appelle f() avec un seul argument - # (qui est lui-même un tuple à 3 valeurs) - -f(()) # appelle f() avec un tuple vide - - -(a) # juste la valeur de a entre parenthèses -(a,) # un tuple à un élément, qui vaut la valeur de a -``` - -## On peut aussi déstructurer des listes - -```python ->>> fruits = ["pomme", "banane", "orange"] ->>> premier, deuxième, troisième = fruits ->>> premier -"pomme" ->>> deuxième -"banane" ->>> troisième -"orange" -``` -On dit aussi: unpacking - -## Utilisations des tuples - -Pour simplifier des conditions: - -```python -# Avant -if ( - ma_valeur == "nord" or - ma_valeur == "sud" or - ma_valeur == "ouest" or - ma_valeur == "est"): - print("direction", ma_valeur) -``` - - -```python -# Après -if ma_valeur in ("nord", "sud", "est", "ouest"): - print("direction", ma_valeur) -``` - -## Pour retourner plusieurs valeurs - -```python -def tire_carte(): - valeur = "10" - couleur = "trèfle" - return (valeur, couleur) - -v, c = tire_carte() -print(v, "de", c) -# 10 de trèfle -``` - -Ce n'est pas une nouvelle syntaxe, juste de la manipulation de tuples! diff --git a/cours/source/10-tuples/index.rst b/cours/source/10-tuples/index.rst index aff8911..cb1f0d1 100644 --- a/cours/source/10-tuples/index.rst +++ b/cours/source/10-tuples/index.rst @@ -1,4 +1,151 @@ -+++ -title = "Chapitre 10 - tuples" -weight = 10 -+++ +Chapitre 10 - tuples +===================== +Définition +------------ + +Un tuple est un ensemble *ordonné* et *immuable* d'éléments. Le nombre, l'ordre et la valeur des éléments sont fixes. + +Création de tuples +------------------ + +Avec des parenthèses:: + + tuple_vide = () + tuple_à_un_élement = (1,) # notez la virgule + tupble_à_deux_éléments = (1, 2) # on dit aussi: "couple" +Sauf pour le tuple vide, c'est la *virgule* qui fait le tuple + +Note: tous les tuples sont truthy, sauf les tuples vides. + +Tuples hétérogènes +------------------- + +Comme les listes, les tuples peuvent contenir des éléments de types différents:: + + # Un entier et une string + mon_tuple = (42, "bonjour") + + # Un entier et un autre tuple + mon_tuple = (21, (True, "au revoir")) + +Accès +----- + +Avec ``[]`` et l'index de l'élément dans le tuple:: + + mon_tuple = (42, "bonjour") + mon_tuple[0] + 42 + mon_tuple[1] + "bonjour" + +Modification +------------ + +Interdit:: + + mon_tuple = (42, "bonjour") + mon_tuple[0] = 44 + TypeError: 'tuple' object does not support item assignment + + +Test d'appartenance +------------------- + +Avec ``in``: + + >>> mon_tuple = (42, 14) + >>> 42 in mon_tuple + True + + >>> 14 in mon_tuple + True + >>> 13 in mon_tuple + False + +Déstructuration +---------------- + +Créer plusieurs variables en une seule ligne:: + + >>> couple = ("Batman", "Robin") + >>> héros, side_kick = couple + >>> héros + 'Batman' + >>> side_kick + 'Robin' + + +Quelques erreurs classiques +--------------------------- + +.. code-block:: python + + >>> héros, side_kick, ennemi = couple + ValueError (3 != 2) + + >>> (héros,) = couple + ValueError (1 != 2) + + # Gare à la virgule: + >>> héros, = couple + ValueError (1 != 2) + +Pièges +------ + +.. code-block:: + + f(a, b, c) # appelle f() avec trois arguments + + f((a, b, c)) # appelle f() avec un seul argument + # (qui est lui-même un tuple à 3 valeurs) + + f(()) # appelle f() avec un tuple vide + + + (a) # juste la valeur de a entre parenthèses + (a,) # un tuple à un élément, qui vaut la valeur de a + +On peut aussi déstructurer des listes:: + + >>> fruits = ["pomme", "banane", "orange"] + >>> premier, deuxième, troisième = fruits + >>> premier + "pomme" + >>> deuxième + "banane" + >>> troisième + "orange" + +On dit aussi: unpacking + +Utilisations des tuples +------------------------ + +Pour simplifier des conditions:: + + # Avant: + if ( + ma_valeur == "nord" or + ma_valeur == "sud" or + ma_valeur == "ouest" or + ma_valeur == "est"): + print("direction", ma_valeur) + + # Après: + if ma_valeur in ("nord", "sud", "est", "ouest"): + print("direction", ma_valeur) + +Pour retourner plusieurs valeurs:: + + def tire_carte(): + valeur = "10" + couleur = "trèfle" + return (valeur, couleur) + + v, c = tire_carte() + print(v, "de", c) + # 10 de trèfle + +Ce n'est pas une nouvelle syntaxe, juste de la manipulation de tuples! diff --git a/cours/source/11-classes-01/01-classes.rst b/cours/source/11-classes-01/01-classes.rst deleted file mode 100644 index b50c94a..0000000 --- a/cours/source/11-classes-01/01-classes.rst +++ /dev/null @@ -1,319 +0,0 @@ -+++ -title = "Classes" -weight = 1 -+++ - -# Classes - -Ce qu’on a vu jusqu’ici: - -* Des types simples (entiers, booléens, ...) -* Des structures de données (listes, dictionnaires, ...) -* Des fonctions qui manipulent ces types ou ces types -* Des fonctions qui s’appellent les unes les autres - -On appelle cet ensemble de concepts, cette façon d'écrire du code, un *paradigme* - -et c'est un paradigme *procédural*. - -On va passer à un autre paradigme: l'*orienté objet*. - -## Orienté objet - une première définition - -Un "objet" informatique *représente* un véritable "objet" physique -dans le vrai monde véritable. - -Ce n'est pas une très bonne définition: - -1. Ce n'est pas nécessaire -2. Ce n'est même pas forcément souhaitable! - -Je le mentionne juste parce que c'est une idée reçue très répandue. - -## Orienté objet - 2ème définition - -Une meilleure définition, c'est de dire que la programmation -orientée objet permet de mettre au même endroit: - -* des données -* des fonctions qui opèrent sur ces données - -L'important c'est que les deux aillent ensemble! - -*Note: ce n'est pas **la** meilleure définition de l'orienté objet, mais on s'en contentera -pour le moment ...* - - -## Les classes - -On va parler *d'une* façon de faire de l'orienté objet: avec des classes. - -Mais notez bien qu'on peut faire de l'orienté objet *sans* classes! - -## Le plan de construction - -On dit souvent qu'en Python, "tout est objet". - -Pour bien comprendre cela, il faut d'abord parler des *classes* et des *instances de classes*. - -Une classe est un *plan de construction*, et est définie ainsi: - -```python -class MaClasse: - # du code ici -``` - -Comme les fonctions, les classes contienent un *corps*, qui est le bloc *identé* en dessous -du mot-clé `class`, de nom de la classe et du `:` en fin de ligne. - -Les classes sont utilisées pour construire des *instances*. - -## Créons des instances - -On peut faire un plan de construction vide avec le mot clé pass: - -```python -class MaClasse: - pass -``` - -Dans ce cas, on crée une instance en mettant le nom de la classe suivi d'une paire de parenthèses - -un peu comme pour appeler une fonction: - -```python ->>> mon_instance = MaClasse() -``` - -Ici, `mon_instance` est une *instance* de la classe `MaClasse`. - -## Attributs - -Les attributs sont des éléments **nommés** à *l'intérieur* d'une instance. - -On peut y accéder avec la syntaxe `.`: - -```python -y = a.x -``` - -Ici, `y` est l'attribut `x` de l'instance `a`. - -Les attributs peuvent être des fonctions: - -```python -func = a.x -func(10) -``` - -Ici, on crée une variable `func` qui prend la valeur de l'attribut `x` dans l'instance `a`, puis -on l'appelle avec l'argument `10` à la ligne suivante. - -Le code suivant fait exactement la même chose, mais avec une ligne de moins: - -```python -a.x(10) -``` - -On peut *créer* des attributs dans *n'importe quel instance*, en utilisant l'*assignation*: - -```python ->>> mon_instance = MaClasse() - -# Création de l'attribut `x` dans `mon_instance` ->>> mon_instance.x = 42 - -# Accés à l'attribut `x` dans `mon_instance` ->>> mon_instance.mon_attribut -42 -``` - -## Méthodes - définition - -On peut aussi mettre des *méthodes* dans des classes. - -On utilise `def`, comme pour les fonctions, mais les méthodes *doivent* avoir au -moins un argument appelé `self`, et être à l'intérieur du bloc de la classe: - -```python -class MaClasse: - def ma_méthode(self): - return 42 -``` - -## Méthodes - appel - -Une méthode ne peut être appelée que depuis une *instance* de -la classe: - -```python -class MaClasse: - def ma_méthode(self): - return 42 ->>> ma_méthode() -Erreur - ->>> mon_instance = MaClasse() ->>> mon_instance.ma_méthode() -42 -``` - -Notez qu'on ne passe *pas* d'argument quand on apelle `ma_méthode` depuis l'instance. - - -## Méthodes et attributs - -`self` *prend la valeur de l'instance courante* quand la méthode est appelée. - -On peut le voir en utilisant des attributs: - -```python -class MaClasse: - def affiche_attribut_x(self): - # Accès à l'attribut `x` dans `self` - print(self.x) - - ->>> mon_instance = MaClasse() ->>> mon_instance.x = 42 ->>> mon_instance.affiche_attribut_x() -42 -``` - -On peut aussi *créer* des attributs dans une méthode: - -```python -class MaClasse: - def crée_attribut_x(self): - self.x = 42 - def affiche_attribut_x(self): - print(self.x) - ->>> mon_instance = MaClasse() ->>> mon_instance.affiche_attribut_x() -# Erreur: `mon_instance` n'a pas d'attribut `x` - ->>> mon_instance.crée_attribut_x() ->>> mon_instance.affiche_attribut_x() -42 -``` - -Les méthodes peuveunt aussi prendre plusieurs arguments, en plus de `self` - mais `self` doit -toujours être le premier argument. - -Par example, pour créer un attribut avec une certaine valeur: - - -```python -class MaClasse - def crée_attribut_x(self, valeur_de_x): - self.x = valeur_de_x - - def affiche_attribut_x(self); - print(self.x) - ->>> mon_instance = MaClasse() ->>> mon_instance.crée_attribut_x(42) ->>> mon_instance.affiche_attribut_x() -42 -``` - -## Méthodes appelant d'autres méthodes - -Comme les méthodes sont *aussi* des attributs, les méthodes d'une instance peuvent s'appeler -les unes les autres: - -```python -class MaClasse: - def méthode_1(self): - print("démarrage de la méthode 1") - print("la méthode 1 affiche bonjour") - print("bonjour") - print("fin de la méthode 1") - - - def méthode_2(self): - print("la méthode 2 appelle la méthode 1") - self.méthode_1() - print("fin de la méthode 2") -``` - - -```python ->>> mon_instance = MaClasse() ->>> mon_instance.méthode_2() -``` - -```text -la méthode 2 appelle la méthode 1 -démarrage de la méthode 1 -la méthode 1 affiche bonjour -bonjour -fin de la méthode 1 -fin de la méthode 2 -``` - -## Une méthode spéciale - -Si vous définissez une méthode `__init__`, celle-ci est appelée *automatiquement* -quand l'instance est construite. - -On dit que c'est une méthode "magique" parce qu'elle fait quelque chose _sans_ qu'on -l'appelle explicitement. - -On utilise souvent `__init__` pour créer des attributs - - -```python -class MaClasse: - def __init__(self): - self.x = 1 - self.y = 2 - ->>> mon_instance = MaClasse() - -# __init__ est appelée automatiquement! ->>> mon_instance.x -1 ->>> mon_instance.y -2 -``` - - -On prend souvent les *valeurs* des attributs à créer en arguments de la méthode `__init__ `. - -```python -class MaClasse: - def __init__(self, x, y): - self.x = x - self.y = y -``` - -Dans ce cas, les arguments de la méthode `__init__` apparaissent à l'intérieur des parenthèses après le -nom de la classe: - -``` ->>> mon_instance = MaClasse(3, 4) ->>> mon_instance.x -3 ->>> mon_instance.y -4 -``` - -*Pour cette raison, `__init__` est souvent appelé le _constructeur_ de la classe.* - -## Récapitulatif - -* Classe: plan de construction -* Instance: valeur issue d'une classe -* Attribut: variable dans une instance -* Méthode: fonction dans une instance (qui prend `self` en premier argument) -* `__init__`: méthode magique appelée automatiquement pendant l'instanciation - - -## Classes et programmation orienté objet - -Ainsi, on peut ranger au même endroit des données et des fonctions opérant sur ces données. - -Les données sont les attributs, et les fonctions opérant sur ces attributs sont les méthodes. - -On peut ainsi séparer les *responsabilités* à l'intérieur d'un code en les répartissant -entres plusieurs classes. diff --git a/cours/source/11-classes-01/index.rst b/cours/source/11-classes-01/index.rst index a237f17..a195fd6 100644 --- a/cours/source/11-classes-01/index.rst +++ b/cours/source/11-classes-01/index.rst @@ -1,4 +1,296 @@ -+++ -title = "Chapitre 11 - Classes (1ère partie)" -weight = 11 -+++ +Chapitre 11 - Classes (1ère partie) +=================================== + +Ce qu’on a vu jusqu’ici: + +* Des types simples (entiers, booléens, ...) +* Des structures de données (listes, dictionnaires, ...) +* Des fonctions qui manipulent ces types ou ces types +* Des fonctions qui s’appellent les unes les autres + +On appelle cet ensemble de concepts, cette façon d'écrire du code, un *paradigme* - +et c'est un paradigme *procédural*. + +On va passer à un autre paradigme: l'*orienté objet*. + +Orienté objet - une première définition +--------------------------------------- + +Un "objet" informatique *représente* un véritable "objet" physique +dans le vrai monde véritable. + +Ce n'est pas une très bonne définition: + +1. Ce n'est pas nécessaire +2. Ce n'est même pas forcément souhaitable! + +Je le mentionne juste parce que c'est une idée reçue très répandue. + +Orienté objet - 2ème définition +-------------------------------- + +Une meilleure définition, c'est de dire que la programmation +orientée objet permet de mettre au même endroit: + +* des données +* des fonctions qui opèrent sur ces données + +L'important c'est que les deux aillent ensemble! + +*Note: ce n'est pas **la** meilleure définition de l'orienté objet, mais on s'en contentera +pour le moment ...* + + +Les classes +----------- + +On va parler *d'une* façon de faire de l'orienté objet: avec des classes. + +Mais notez bien qu'on peut faire de l'orienté objet *sans* classes! + +Le plan de construction +----------------------- + +On dit souvent qu'en Python, "tout est objet". + +Pour bien comprendre cela, il faut d'abord parler des *classes* et des *instances de classes*. + +Une classe est un *plan de construction*, et est définie ainsi:: + + class MaClasse: + # du code ici + +Comme les fonctions, les classes contienent un *corps*, qui est le bloc *identé* en dessous +du mot-clé `class`, de nom de la classe et du `:` en fin de ligne. + +Les classes sont utilisées pour construire des *instances*. + +Créons des instances +--------------------- + +On peut faire un plan de construction vide avec le mot clé pass:: + + class MaClasse: + pass + +Dans ce cas, on crée une instance en mettant le nom de la classe suivi d'une paire de parenthèses - +un peu comme pour appeler une fonction:: + + mon_instance = MaClasse() + +Ici, ``mon_instance`` est une *instance* de la classe ``MaClasse``. + +Attributs +--------- + +Les attributs sont des éléments **nommés** à *l'intérieur* d'une instance. + +On peut y accéder avec la syntaxe ``.``:: + + y = a.x + +Ici, ``y`` est l'attribut ``x`` de l'instance ``a``. + +Les attributs peuvent être des fonctions:: + + func = a.x + func(10) + +Ici, on crée une variable ``func`` qui prend la valeur de l'attribut ``x`` dans l'instance ``a``, puis +on l'appelle avec l'argument ``10`` à la ligne suivante. + +Le code suivant fait exactement la même chose, mais avec une ligne de moins:: + + a.x(10) + +On peut *créer* des attributs dans *n'importe quel instance*, en utilisant l'*assignation*:: + + >>> mon_instance = MaClasse() + + # Création de l'attribut `x` dans `mon_instance` + >>> mon_instance.x = 42 + + # Accés à l'attribut `x` dans `mon_instance` + >>> mon_instance.mon_attribut + 42 + +Méthodes - définition +---------------------- + +On peut aussi mettre des *méthodes* dans des classes. + +On utilise `def`, comme pour les fonctions, mais les méthodes *doivent* avoir au +moins un argument appelé `self`, et être à l'intérieur du bloc de la classe:: + + class MaClasse: + def ma_méthode(self): + return 42 + +Méthodes - appel +---------------- + +Une méthode ne peut être appelée que depuis une *instance* de +la classe:: + + class MaClasse: + def ma_méthode(self): + return 42 + >>> ma_méthode() + Erreur + + >>> mon_instance = MaClasse() + >>> mon_instance.ma_méthode() + 42 + +Notez qu'on ne passe *pas* d'argument quand on apelle `ma_méthode` depuis l'instance. + + +Méthodes et attributs +--------------------- + +``self`` *prend la valeur de l'instance courante* quand la méthode est appelée. + +On peut le voir en utilisant des attributs:: + + class MaClasse: + def affiche_attribut_x(self): + # Accès à l'attribut `x` dans `self` + print(self.x) + + + >>> mon_instance = MaClasse() + >>> mon_instance.x = 42 + >>> mon_instance.affiche_attribut_x() + 42 + +On peut aussi *créer* des attributs dans une méthode:: + + class MaClasse: + def crée_attribut_x(self): + self.x = 42 + def affiche_attribut_x(self): + print(self.x) + + >>> mon_instance = MaClasse() + >>> mon_instance.affiche_attribut_x() + # Erreur: `mon_instance` n'a pas d'attribut `x` + + >>> mon_instance.crée_attribut_x() + >>> mon_instance.affiche_attribut_x() + 42 + +Les méthodes peuveunt aussi prendre plusieurs arguments, en plus de ``self`` - mais ``self`` doit +toujours être le premier argument. + +Par example, pour créer un attribut avec une certaine valeur:: + + + class MaClasse + def crée_attribut_x(self, valeur_de_x): + self.x = valeur_de_x + + def affiche_attribut_x(self); + print(self.x) + + >>> mon_instance = MaClasse() + >>> mon_instance.crée_attribut_x(42) + >>> mon_instance.affiche_attribut_x() + 42 + +Méthodes appelant d'autres méthodes +------------------------------------ + +Comme les méthodes sont *aussi* des attributs, les méthodes d'une instance peuvent s'appeler +les unes les autres:: + + class MaClasse: + def méthode_1(self): + print("démarrage de la méthode 1") + print("la méthode 1 affiche bonjour") + print("bonjour") + print("fin de la méthode 1") + + + def méthode_2(self): + print("la méthode 2 appelle la méthode 1") + self.méthode_1() + print("fin de la méthode 2") + + + >>> mon_instance = MaClasse() + >>> mon_instance.méthode_2() + +.. code-block:: + + la méthode 2 appelle la méthode 1 + démarrage de la méthode 1 + la méthode 1 affiche bonjour + bonjour + fin de la méthode 1 + fin de la méthode 2 + +Une méthode spéciale +--------------------- + +Si vous définissez une méthode nomée ``__init__``, celle-ci est appelée *automatiquement* +quand l'instance est construite. + +On dit que c'est une méthode "magique" parce qu'elle fait quelque chose _sans_ qu'on +l'appelle explicitement. + +On utilise souvent ``__init__`` pour créer des attributs:: + + + class MaClasse: + def __init__(self): + self.x = 1 + self.y = 2 + + >>> mon_instance = MaClasse() + + # __init__ est appelée automatiquement! + >>> mon_instance.x + 1 + >>> mon_instance.y + 2 + +On prend souvent les *valeurs* des attributs à créer en arguments de la méthode ``__init__``:: + + class MaClasse: + def __init__(self, x, y): + self.x = x + self.y = y + +Dans ce cas, les arguments de la méthode ``__init__`` apparaissent à l'intérieur des parenthèses après le +nom de la classe:: + + >>> mon_instance = MaClasse(3, 4) + >>> mon_instance.x + 3 + >>> mon_instance.y + 4 + +.. note:: + + Pour cette raison, __init__ est souvent appelé le **constructeur** de la classe. + +Récapitulatif +------------- + +* Classe: plan de construction +* Instance: valeur issue d'une classe +* Attribut: variable dans une instance +* Méthode: fonction dans une instance (qui prend `self` en premier argument) +* ``__init__``: méthode magique appelée automatiquement pendant l'instanciation + + +Classes et programmation orienté objet +-------------------------------------- + +Ainsi, on peut ranger au même endroit des données et des fonctions opérant sur ces données. + +Les données sont les attributs, et les fonctions opérant sur ces attributs sont les méthodes. + +On peut ainsi séparer les *responsabilités* à l'intérieur d'un code en les répartissant +entres plusieurs classes. + diff --git a/cours/source/12-modules-01/01-modules.rst b/cours/source/12-modules-01/01-modules.rst deleted file mode 100644 index 5ea7a96..0000000 --- a/cours/source/12-modules-01/01-modules.rst +++ /dev/null @@ -1,148 +0,0 @@ -+++ -title = "Modules" -weight = 1 -+++ - -# Modules - -## Un fichier = un module - -Et oui, vous faites des modules sans le savoir depuis le début :) - -Un fichier `foo.py` correspond *toujours* module `foo` - -**Attention: Ce n'est pas tout à fait réciproque. Le module `foo` peut venir d'autre chose -qu'un fichier foo.py.** - -# Importer un module - -Ou: accéder à du code provenant d'un *autre* fichier source. - -Imaginons un fichier `bonjour.py` contenant seulement une assignation -d'une variable `a` à l'entier 42 : - -```python -# Dans bonjour.py -a = 42 -``` - -On peut accéder à cette variable en important le module, par -exemple depuis l'interpréteur, en utilisant le mot-clé `import` -suivi du nom du module: - -```python -$ python ->>> import bonjour ->>> bonjour.a -42 -``` - -Notez que pour que cela fonctionne: - -* Le nom du module est écrit directement, ce n'est *pas* une - chaîne de caractères. -* Il faut lancer la commande `python` sans argument -* Il faut la lancer depuis le répertoire qui contient `bonjour.py`. - -On voit que l'assignation de la variable `a` dans `bonjour.py` est devenue -un *attribut* du module `bonjour` lorsque `bonjour` a été importé - -\newpage - -Si maintenant on rajoute une fonction `dire_bonjour` dans `bonjour.py`: - -```python -# toujours dans bonjour.py -a = 42 -def dire_bonjour(): - print("Bonjour!") -``` - -On peut appeler la fonction `dire_bonjour` depuis l'interpréteur en accédant -à l'attribut `dire_bonjour` du module `bonjour`: - -```python ->>> import bonjour ->>> bonjour.dire_bonjour() -Bonjour! -``` - -## Différence avec la commande python - -Notez bien que lancer l'interpréteur et taper `import bonjour` dedans n'est pas -la même chose que lancer `python bonjour.py`. - -Dans le deuxième cas, tout le code dans `bonjour.py` est exécuté, puis la commande python -se termine. - -Dans le cas de l'interpréteur, on peut utiliser tous les attributs du module et appeler -les fonctions autant de fois qu'on veut: - -```python ->>> import bonjour ->>> bonjour.dire_bonjour() -Bonjour! ->>> bonjour.dire_bonjour() -Bonjour! -``` - -On peut aussi modifier les valeurs des attributs: - -```python ->>> import bonjour ->>> bonjour.a -42 ->>> bonjour.a = 36 ->>> bonjour.a -36 -``` - - - -## Les imports ne sont faits qu'une seule fois - -Il est important de noter que le code à l'intérieur d'un -module n'est *pas* ré-éxécuté si le module a déjà été -importé auparavant. - -On peut le voir en mettant du code dans `bonjour.py`, -en plus des simples définitions de fonctions et assignations -de variables -```python -# Dans bonjour.py -print("Je suis le module bonjour et tu viens de m’importer") -``` - -```python ->>> import bonjour -Je suis le module foo et tu viens de m’importer ->>> import bonjour - -``` - -Il faudra donc redémarrer l'interpréteur à chaque fois que le code dans `bonjour.py` change. - -## La bibliothèque standard - -La bibliothèque standard est une collection de modules directement utilisables fournis à l'installation de Python. - -Exemple: `sys`, `random`, ... - -Toute la bibliothèque standard est documentée - et la traduction en Français est en cours: - -https://docs.python.org/fr/3/library/index.html - -Mettez ce lien dans vos favoris - il vous sera très utile. - -## Quelques exemples de modules de la bibliothèque standard - -### Easter eggs - -(Ou fonctionnalités cachées) - -* `import antigravity` -* `import this` - -Je vous laisse découvrir ce que fait le premier. Quant au deuxième, il contient -une liste de préceptes que la plupart des développeurs Python s'efforcent de -respecter. On en reparlera ... diff --git a/cours/source/12-modules-01/index.rst b/cours/source/12-modules-01/index.rst index 505e713..dea7cad 100644 --- a/cours/source/12-modules-01/index.rst +++ b/cours/source/12-modules-01/index.rst @@ -1,4 +1,135 @@ -+++ -title = "Chapitre 12 - Modules - 1ère partie" -weight = 12 -+++ +Chapitre 12 - Modules - 1ère partie +=================================== + +Un fichier = un module +------------------------ + +Et oui, vous faites des modules sans le savoir depuis le début :) + +Un fichier `foo.py` correspond *toujours* module `foo` + +**Attention: Ce n'est pas tout à fait réciproque. Le module `foo` peut venir d'autre chose +qu'un fichier foo.py.** + +Importer un module +------------------ + +Ou: accéder à du code provenant d'un *autre* fichier source. + +Imaginons un fichier `bonjour.py` contenant seulement une assignation +d'une variable `a` à l'entier 42 :: + + # Dans bonjour.py + a = 42 + +On peut accéder à cette variable en important le module, par +exemple depuis l'interpréteur, en utilisant le mot-clé `import` +suivi du nom du module:: + + $ python + >>> import bonjour + >>> bonjour.a + 42 + +Notez que pour que cela fonctionne: + +* Le nom du module est écrit directement, ce n'est *pas* une + chaîne de caractères. +* Il faut lancer la commande `python` sans argument +* Il faut la lancer depuis le répertoire qui contient `bonjour.py`. + +On voit que l'assignation de la variable `a` dans `bonjour.py` est devenue +un *attribut* du module `bonjour` lorsque `bonjour` a été importé + + +Si maintenant on rajoute une fonction ``dire_bonjour`` dans ``bonjour.py``:: + + # toujours dans bonjour.py + a = 42 + def dire_bonjour(): + print("Bonjour!") + +On peut appeler la fonction ``dire_bonjour`` depuis l'interpréteur en accédant +à l'attribut ``dire_bonjour`` du module ``bonjour``:: + + >>> import bonjour + >>> bonjour.dire_bonjour() + Bonjour! + +Différence avec la commande python +----------------------------------- + +Notez bien que lancer l'interpréteur et taper `import bonjour` dedans n'est pas +la même chose que lancer `python bonjour.py`. + +Dans le deuxième cas, tout le code dans `bonjour.py` est exécuté, puis la commande python +se termine. + +Dans le cas de l'interpréteur, on peut utiliser tous les attributs du module et appeler +les fonctions autant de fois qu'on veut:: + + >>> import bonjour + >>> bonjour.dire_bonjour() + Bonjour! + >>> bonjour.dire_bonjour() + Bonjour! + +On peut aussi modifier les valeurs des attributs:: + + >>> import bonjour + >>> bonjour.a + 42 + >>> bonjour.a = 36 + >>> bonjour.a + 36 + + +Les imports ne sont faits qu'une seule fois +------------------------------------------- + +Il est important de noter que le code à l'intérieur d'un +module n'est *pas* ré-éxécuté si le module a déjà été +importé auparavant. + +On peut le voir en mettant du code dans `bonjour.py`, +en plus des simples définitions de fonctions et assignations +de variables:: + + # Dans bonjour.py + print("Je suis le module bonjour et tu viens de m’importer") + + >>> import bonjour + Je suis le module foo et tu viens de m’importer + >>> import bonjour + + +Il faudra donc redémarrer l'interpréteur à chaque fois que le code dans `bonjour.py` change. + +a bibliothèque standard +------------------------ + +La bibliothèque standard est une collection de modules directement utilisables fournis à l'installation de Python. + +Exemple: ``sys``, ``random``, ... + +Toute la bibliothèque standard est documentée - et la traduction en Français est en cours: + +https://docs.python.org/fr/3/library/index.html + +Mettez ce lien dans vos favoris - il vous sera très utile. + +Quelques exemples de modules de la bibliothèque standard +--------------------------------------------------------- + +Easter eggs +++++++++++++ + +(Ou fonctionnalités cachées) + +* ``import antigravity`` +* ``import this`` + +Je vous laisse découvrir ce que fait le premier. Quant au deuxième, il contient +une liste de préceptes que la plupart des développeurs Python s'efforcent de +respecter. On en reparlera ... + diff --git a/cours/source/13-classes-02/01-rappels.rst b/cours/source/13-classes-02/01-rappels.rst index d2099e4..5fc38f4 100644 --- a/cours/source/13-classes-02/01-rappels.rst +++ b/cours/source/13-classes-02/01-rappels.rst @@ -1,186 +1,177 @@ -+++ -title = "Rappels" -weight = 1 -+++ +Rappels +====== -# Rappels +// TODO: drop? -_Note: ceci est surtout un rappel du chapitre 11. N'hésitez pas à vous y -reporter si les exemples de code ne vous paraissent pas clairs._ +.. note:: + ceci est surtout un rappel du chapitre 11. N'hésitez pas à vous y + reporter si les exemples de code ne vous paraissent pas clairs. -## Classes vides -Définition: -```python -class MaClasse: - pass -``` +Classes vides +------------- -Instanciation: -```python ->>> instance_1 = MaClasse() -``` +Définition:: + + class MaClasse: + pass -## Attributs +Instanciation:: + + instance_1 = MaClasse() + +Attributs +--------- Un attribut est une variable _à l'intérieur_ d'autre chose (par exemple une instance de classe). -La syntaxe consiste en l'instance à gauche et l'attribut à droite après un point: +La syntaxe consiste en l'instance à gauche et l'attribut à droite après un point:: -```python ->>> mon_instance = MaClasse() -# création de l'attribut `x` dans mon_instance: ->>> mon_instance.x = 42 -# accès à l'attribut `x` dans mon_instance: ->>> mon_instance.x -42 -``` + >>> mon_instance = MaClasse() + # création de l'attribut `x` dans mon_instance: + >>> mon_instance.x = 42 + # accès à l'attribut `x` dans mon_instance: + >>> mon_instance.x + 42 -## Méthodes +Méthodes +-------- Une méthode est une fonction définie à l'intérieur d'une classe: -Définition: -```python -class MaClasse: - def ma_méthode(self): - return 42 -``` - -Les méthodes sont des attributs des instances de classes: +Définition:: -```python -class MaClasse: - def ma_méthode(self): + class MaClasse: + def ma_méthode(self): return 42 ->>> ma_méthode() -Erreur ->>> mon_instance = MaClasse() ->>> mon_instance.ma_méthode() -42 + +Les méthodes sont des attributs des instances de classes:: + + class MaClasse: + def ma_méthode(self): + return 42 + >>> ma_méthode() + Erreur + >>> mon_instance = MaClasse() + >>> mon_instance.ma_méthode() + 42 ``` -## self +self +---- -`self` *prend la valeur de l'instance courante* quand la méthode est appelée. +`self` *prend la valeur de l'instance courante* quand la méthode est appelée.:: -```python -class MaClasse: - def affiche_attribut_x(self): - print(self.x) + class MaClasse: + def affiche_attribut_x(self): + print(self.x) ->>> mon_instance = MaClasse() ->>> mon_instance.x = 42 ->>> mon_instance.affiche_attribut_x() -42 -``` + >>> mon_instance = MaClasse() + >>> mon_instance.x = 42 + >>> mon_instance.affiche_attribut_x() + 42 -On peut aussi *créer* des attributs dans une méthode: +On peut aussi *créer* des attributs dans une méthode:: -```python -class MaClasse: - def crée_attribut_x(self): - self.x = 42 - def affiche_attribut_x(self): - print(self.x) + class MaClasse: + def crée_attribut_x(self): + self.x = 42 + def affiche_attribut_x(self): + print(self.x) ->>> mon_instance = MaClasse() ->>> mon_instance.affiche_attribut_x() -# Erreur: `mon_instance` n'a pas d'attribut `x` + >>> mon_instance = MaClasse() + >>> mon_instance.affiche_attribut_x() + # Erreur: `mon_instance` n'a pas d'attribut `x` ->>> mon_instance.crée_attribut_x() ->>> mon_instance.affiche_attribut_x() -42 -``` + >>> mon_instance.crée_attribut_x() + >>> mon_instance.affiche_attribut_x() + 42 -## Méthodes avec arguments +Méthodes avec arguments +------------------------ -```python -class MaClasse - def crée_attribut_x(self, valeur_de_x): - self.x = valeur_de_x +.. code-block:: - def affiche_attribut_x(self); - print(self.x) + class MaClasse + def crée_attribut_x(self, valeur_de_x): + self.x = valeur_de_x ->>> mon_instance = MaClasse() ->>> mon_instance.crée_attribut_x(42) ->>> mon_instance.affiche_attribut_x() -42 -``` + def affiche_attribut_x(self); + print(self.x) -## Méthodes appelant d'autres méthodes + >>> mon_instance = MaClasse() + >>> mon_instance.crée_attribut_x(42) + >>> mon_instance.affiche_attribut_x() + 42 -```python -class MaClasse: - def méthode_1(self): - print("démarrage de la méthode 1") - print("la méthode 1 affiche bonjour") - print("bonjour") - print("fin de la méthode 1") +Méthodes appelant d'autres méthodes +------------------------------------ +.. code-block:: - def méthode_2(self): - print("la méthode 2 appelle la méthode 1") - self.méthode_1() - print("fin de la méthode 2") -``` + class MaClasse: + def méthode_1(self): + print("démarrage de la méthode 1") + print("la méthode 1 affiche bonjour") + print("bonjour") + print("fin de la méthode 1") -```python ->>> mon_instance = MaClasse() ->>> mon_instance.méthode_2() -``` + def méthode_2(self): + print("la méthode 2 appelle la méthode 1") + self.méthode_1() + print("fin de la méthode 2") -```text -la méthode 2 appelle la méthode 1 -démarrage de la méthode 1 -la méthode 1 affiche bonjour -bonjour -fin de la méthode 1 -fin de la méthode 2 -``` -## Constructeur sans arguments + >>> mon_instance = MaClasse() + >>> mon_instance.méthode_2() + +.. code-block:: + + la méthode 2 appelle la méthode 1 + démarrage de la méthode 1 + la méthode 1 affiche bonjour + bonjour + fin de la méthode 1 + fin de la méthode 2 -Un constructeur en Python désigne la méthode nomée `__init__`, +Constructeur sans arguments +--------------------------- + +Un constructeur en Python désigne la méthode nomée ``__init__``, quand celle-ci existe. -La méthode `__init__` est appelée automatiquement quand la -classe est instanciée: +La méthode ``__init__`` est appelée automatiquement quand la +classe est instanciée:: -```python -class MaClasse: - def __init__(self): - self.x = 1 - self.y = 2 + class MaClasse: + def __init__(self): + self.x = 1 + self.y = 2 ->>> mon_instance = MaClasse() ->>> mon_instance.x -1 ->>> mon_instance.y -2 -``` + >>> mon_instance = MaClasse() + >>> mon_instance.x + 1 + >>> mon_instance.y + 2 -## Constructeur avec arguments +Constructeur avec arguments +---------------------------- -La méthode `__init__` peut avoir des arguments, +La méthode ``__init__`` peut avoir des arguments, dans ce cas, ceux ci doivent être fournis -lors de l'instanciation: +lors de l'instanciation:: -```python -class MaClasse: - def __init__(self, x, y): - self.x = x - self.y = y -``` + class MaClasse: + def __init__(self, x, y): + self.x = x + self.y = y -```python ->>> mon_instance = MaClasse(3, 4) ->>> mon_instance.x -3 ->>> mon_instance.y -4 -``` + >>> mon_instance = MaClasse(3, 4) + >>> mon_instance.x + 3 + + >>> mon_instance.y + 4 diff --git a/cours/source/13-classes-02/02-couplage.rst b/cours/source/13-classes-02/02-couplage.rst index 379b89f..0852c30 100644 --- a/cours/source/13-classes-02/02-couplage.rst +++ b/cours/source/13-classes-02/02-couplage.rst @@ -1,91 +1,82 @@ -+++ -title = "Couplage" -weight = 2 -+++ +Couplage +======== -# Couplage - -## Définition +Définition +---------- Un couplage décrit une relation entre deux classes. -## Exemple +Exemple +------- Ici on veut représenter des chats et des humains qui adoptent (on non) des chats. Tous les chats ont un nom, et tous les humains ont un prénom. -On peut utiliser pour cela deux classes: `Chat` et `Humain`: +On peut utiliser pour cela deux classes: `Chat` et `Humain`:: -```python -class Chat: - def __init__(self, nom): - self.nom = nom + class Chat: + def __init__(self, nom): + self.nom = nom ->>> chat = Chat("Monsieur Moustaches") ->>> chat.nom -'Monsieur Moustaches' -``` + >>> chat = Chat("Monsieur Moustaches") + >>> chat.nom + 'Monsieur Moustaches' -```python -class Humain: - def __init__(self, prénom): - self.prénom = prénom ->>> alice = Humain(prénom="Alice") ->>> alice.prénom -"Alice" -``` + class Humain: + def __init__(self, prénom): + self.prénom = prénom + >>> alice = Humain(prénom="Alice") + >>> alice.prénom + "Alice" Maintenant on veut que les humains puissent adopter des chats. -Pour cela, on peut rajouter la méthode `adopte` dans la classe -`Humain`. +Pour cela, on peut rajouter la méthode ``adopte`` dans la classe +``Humain``. Cette méthode va prendre un argument - une instance de la -classe `Chat`: - -```python -class Humain: - def __init__(self, prénom): - self.prénom = prénom - - def adopte(self, chat): - print(self.prénom, "adopte un chat") - ->>> boule_de_poils = Chat("Boule de Poils") ->>> alice = Humain("Alice") ->>> alice.adopte(boule_de_poils) -"Alice adopte un chat" -``` - -On peut accéder au nom du chat depuis la méthode `adopte`, -en utilisant la syntaxe `nom.attribut` vue précédemment: - -```python -class Humain: - def __init__(self, prénom): - self.prénom = prénom - - def adopte(self, chat): - print(self.prénom, "adopte", chat.nom) - ->>> boule_de_poils = Chat("Boule de Poils") ->>> alice = Humain("Alice") ->>> alice.adopte(boule_de_poils) -"Alice adopte Boule de Poils" -``` - -## Couplage - -```python -class Humain: - ... - def adopte(self, chat): - print(self.prénom, "adopte", chat.nom) -``` - -Notez également que nous avons écrit `chat.nom`. ainsi, la méthode `adopte()` -ne peut être appelée que part une instance qui a un attribut `nom` - sinon +classe ``Chat``:: + + class Humain: + def __init__(self, prénom): + self.prénom = prénom + + def adopte(self, chat): + print(self.prénom, "adopte un chat") + + >>> boule_de_poils = Chat("Boule de Poils") + >>> alice = Humain("Alice") + >>> alice.adopte(boule_de_poils) + "Alice adopte un chat" + +On peut accéder au nom du chat depuis la méthode ``adopte``, +en utilisant la syntaxe ``nom.attribut`` vue précédemment:: + + class Humain: + def __init__(self, prénom): + self.prénom = prénom + + def adopte(self, chat): + print(self.prénom, "adopte", chat.nom) + + >>> boule_de_poils = Chat("Boule de Poils") + >>> alice = Humain("Alice") + >>> alice.adopte(boule_de_poils) + "Alice adopte Boule de Poils" + +Couplage +-------- + +.. code-block:: + + class Humain: + ... + def adopte(self, chat): + print(self.prénom, "adopte", chat.nom) + +Notez également que nous avons écrit ``chat.nom``. ainsi, la méthode ``adopte()`` +ne peut être appelée que part une instance qui a un attribut ``nom`` - sinon on aura une erreur. -Donc si on modifie la classe `Chat` et qu'on renomme l'attribut `nom` en `surnom` par exemple, -la méthode `adopte()` de la classe `Humain` cessera de fonctionner: on dit -qu'on a un *couplage* entre les classes `Chat` et `Humain`. +Donc si on modifie la classe ``Chat`` et qu'on renomme l'attribut ``nom`` en ``surnom`` par exemple, +la méthode ``adopte()`` de la classe ``Humain`` cessera de fonctionner: on dit +qu'on a un *couplage* entre les classes ``Chat`` et ``Humain``. diff --git a/cours/source/13-classes-02/03-composition.rst b/cours/source/13-classes-02/03-composition.rst index eed6bf4..e546d36 100644 --- a/cours/source/13-classes-02/03-composition.rst +++ b/cours/source/13-classes-02/03-composition.rst @@ -1,117 +1,106 @@ -+++ -title = "Composition" -weight = 3 -+++ +Composition +============ -# Composition -## Définition +Définition +----------- Une classe à l'intérieur d'une autre classe. -## Dépendances entre fonctions +Dépendances entre fonctions +----------------------------- -Exemple: on veut dessiner un sapin dans le terminal: +Exemple: on veut dessiner un sapin dans le terminal:: -```python -def main(): - largeur = demander_largeur() - dessine_sapin(largeur) + def main(): + largeur = demander_largeur() + dessine_sapin(largeur) -main() -``` + main() -On voit que la fonction `dessine_sapin()` prend un argument `largeur`, qui est retourné -par la fonction `demander_largeur()`. +On voit que la fonction ``dessine_sapin()`` prend un argument ``largeur``, qui est retourné +par la fonction ``demander_largeur()``. -`dessine_sapin()` doit donc être appelée *après* `demander_largeur()`. On dit que `dessine_sapin()` -_dépend_ de `demander_largeur()`. +``dessine_sapin()`` doit donc être appelée *après* ``demander_largeur()``. On dit que ``dessine_sapin()`` +_dépend_ de ``demander_largeur()``. -## Dépendances entre classes +Dépendances entre classes +------------------------- Un bon moyen d'introduire une dépendance entre deux classes est d'utiliser les constructeurs. -Revoyons la classe Chat: +Revoyons la classe Chat:: -```python -class Chat: - def __init__(self, nom): - self.nom = nome -``` + class Chat: + def __init__(self, nom): + self.nom = nome Comme le constructeur de la classe Chat prend un nom en argument, il est impossible de construire -des chats sans nom: +des chats sans nom:: -```python ->>> chat = Chat() -TypeError: __init__() missing 1 required positional argument: 'nom' -``` + >>> chat = Chat() + TypeError: __init__() missing 1 required positional argument: 'nom' De la même façon, si on veut que tous les enfants aient un chat (pourquoi pas, après tout), on peut -avoir une classe Enfant, dont le constructeur prend une instance de chat en plus du prénom: +avoir une classe Enfant, dont le constructeur prend une instance de chat en plus du prénom:: -```python -class Enfant: - def __init__(self, prénom, chat): - self.prénom = prénom - self.chat = chat + class Enfant: + def __init__(self, prénom, chat): + self.prénom = prénom + self.chat = chat ->>> alice = Enfant("Alice") -TypeError: __init__() missing 1 required positional argument: 'chat' + >>> alice = Enfant("Alice") + TypeError: __init__() missing 1 required positional argument: 'chat' ->>> boule_de_poils = Chat("Boule de Poils") ->>> alice = Enfant("Alice", boule_de_poils) -# OK! -``` + >>> boule_de_poils = Chat("Boule de Poils") + >>> alice = Enfant("Alice", boule_de_poils) + # OK! -## Utilisation de la composition +Utilisation de la composition +----------------------------- Maintenant qu'on vit dans un monde où tous les enfants ont chacun un chat, on peut par exemple consoler tous les enfants en leur demandant de caresser leur chat, chat qui va ronronner et faire plaisir à son propriétaire. -Voici comment on peut coder cela: d'abord, on rajoute les méthodes `caresse()` -et `ronronne()` dans la classe Chat: +voici comment on peut coder cela: d'abord, on rajoute les méthodes ``caresse()`` +et ``ronronne()`` dans la classe chat:: -```python -class Chat: - def __init__(self, nom): - self.nom = nom + class Chat: + def __init__(self, nom): + self.nom = nom - def ronronne(self): - print(self.nom, 'fait: "prrrrr"') + def ronronne(self): + print(self.nom, 'fait: "prrrrr"') - def caresse(self): - self.ronronne() + def caresse(self): + self.ronronne() ->>> boule_de_poils = Chat("Boule de Poils") ->>> boule_de_poils.caresse() -Boule de Poils fait "prrrrr" -``` + >>> boule_de_poils = Chat("Boule de Poils") + >>> boule_de_poils.caresse() + Boule de Poils fait "prrrrr" -Ensuite, on peut rajouter la méthode `console()` dans la classe Enfant, +Ensuite, on peut rajouter la méthode ``console()`` dans la classe Enfant, qui va: -* récupérer l'instance de la classe Chat dans `self` - comme n'importe quel attribut -* puis appeler la méthode `caresse()` de cette instance +* récupérer l'instance de la classe Chat dans ``self`` - comme n'importe quel attribut +* puis appeler la méthode ``caresse()`` de cette instance:: -```python -class Enfant: - def __init__(self, prénom, chat): - self.chat = chat + class Enfant: + def __init__(self, prénom, chat): + self.chat = chat - def console(self): - self.chat.caresse() + def console(self): + self.chat.caresse() ->>> boule_de_poils = Chat("Boule de Poils") ->>> alice = Enfant("Alice", boule_de_poils) -# Alice est triste, on la console ->>> alice.console() -Boule de Poils fait "prrrrr" -# Alice est consolée :) -``` + >>> boule_de_poils = Chat("Boule de Poils") + >>> alice = Enfant("Alice", boule_de_poils) + # Alice est triste, on la console + >>> alice.console() + Boule de Poils fait "prrrrr" + # Alice est consolée :) -On dit parfois qu'on a *délégué* l'implémentation de la méthode `console()` de la classe Enfant -à la méthode `caresse()` de la classe Chat. +On dit parfois qu'on a *délégué* l'implémentation de la méthode ``console()`` de la classe Enfant +à la méthode ``caresse()`` de la classe Chat. diff --git a/cours/source/13-classes-02/index.rst b/cours/source/13-classes-02/index.rst index c840da7..67d6b37 100644 --- a/cours/source/13-classes-02/index.rst +++ b/cours/source/13-classes-02/index.rst @@ -1,4 +1,9 @@ -+++ -title = "Chapitre 13 - Classes (2ème partie)" -weight = 13 -+++ +Chapitre 13 - Classes (2ème partie) +=================================== + +.. toctree:: + :maxdepth: 1 + + 01-rappels + 02-couplage + 03-composition diff --git a/cours/source/14-bibliothèques-01/01-rappels.rst b/cours/source/14-bibliothèques-01/01-rappels.rst index 7097aae..0b55ccb 100644 --- a/cours/source/14-bibliothèques-01/01-rappels.rst +++ b/cours/source/14-bibliothèques-01/01-rappels.rst @@ -1,42 +1,39 @@ -+++ -title = "Introduction" -weight = 1 -+++ +Introduction +============ -# Introduction -## Importer un module +Importer un module +------------------- Souvenez-vous, dans le chapitre 12 nous avons vu que le code suivant Ce code fonctionne s'il y a un ficher `foo.py` quelque part qui contient la fonction -`bar` [^1]: +``bar``::: -```python -import foo -foo.bar() -``` + import foo + foo.bar() Ce fichier peut être présent soit dans le répertoire courant, soit dans la bibliothèque standard Python. -## La variable PATH +La variable PATH +------------------- -Vous connaissez peut-être le rôle de la variable d'environnement `PATH`. Celle-ci contient une liste de chemins, -séparés par le caractère `:` et est utilisée par votre shell pour trouver le chemin complet des commandes que vous lancez. +Vous connaissez peut-être le rôle de la variable d'environnement ``PATH``. Celle-ci contient une liste de chemins, +séparés par le caractère ``:`` et est utilisée par votre shell pour trouver le chemin complet des commandes que vous lancez. Par exemple: -```bash -PATH="/bin:/usr/bin:/usr/sbin" -$ ifconfig -# lance le binaire /usr/sbin/ifconfig -$ ls -# lance le binaire /bin/ls -``` +.. code-block:: console + + PATH="/bin:/usr/bin:/usr/sbin" + $ ifconfig + # lance le binaire /usr/sbin/ifconfig + $ ls + # lance le binaire /bin/ls Le chemin est "résolu" par le shell en parcourant la liste de tout les chemins de la variable `PATH`, et en regardant si le chemin complet existe. La résolution s'arrête dès le premier chemin trouvé. -Par exemple, si vous avez `PATH="/home/user/bin:/usr/bin"` et un fichier `ls` dans `/home/user/bin/ls`, c'est ce fichier-là -(et non `/bin/ls`) qui sera utilisé quand vous taperez `ls`. +Par exemple, si vous avez ``PATH="/home/user/bin:/usr/bin"`` et un fichier ``ls`` dans ``/home/user/bin/ls``, c'est ce fichier-là +(et non ``/bin/ls``) qui sera utilisé quand vous taperez ``ls``. diff --git a/cours/source/14-bibliothèques-01/02-sys.path.rst b/cours/source/14-bibliothèques-01/02-sys.path.rst index e004143..c5cbd80 100644 --- a/cours/source/14-bibliothèques-01/02-sys.path.rst +++ b/cours/source/14-bibliothèques-01/02-sys.path.rst @@ -1,439 +1,57 @@ -+++ -title = "sys.path" -weight = 2 -+++ +sys.path +======== -# sys.path +En Python, il existe une variable ``path`` prédéfinie dans le module ``sys`` qui fonctionne de manière similaire +à la variable d'environnement ``PATH``. -En Python, il existe une variable `path` prédéfinie dans le module `sys` qui fonctionne de manière similaire -à la variable d'environnement `PATH`. +Si j'essaye de l'afficher sur ma machine, voici ce que j'obtiens: -Si j'essaye de l'afficher sur ma machine, voici ce que j'obtiens : + import sys + print(sys.path) -```python -import sys -print(sys.path) -``` +.. code-block:: text -``` -[ - "", - "/usr/lib/python3.8", - "/usr/lib/python3.8/lib-dynload", - "/home/dmerej/.local/lib/python3.8/", - "/usr/lib/python3.8/site-packages", -] -``` + [ + "", + "/usr/lib/python3.8", + "/usr/lib/python3.8/lib-dynload", + "/home/dmerej/.local/lib/python3.8/", + "/usr/lib/python3.8/site-packages", + ] Le résultat dépend: * du système d'exploitation * de la façon dont Python a été installé * et de la présence ou non de certains réportoires. -En fait, `sys.path` est construit dynamiquement par l'interpréteur Python au démarrage. +En fait, ``sys.path`` est construit dynamiquement par l'interpréteur Python au démarrage. -Notez également que `sys.path` commence par une chaîne vide. En pratique, cela signifie que le répertoire courant a la priorité sur tout le reste. +Notez également que ``sys.path`` commence par une chaîne vide. En pratique, cela signifie que le répertoire courant a la priorité sur tout le reste. -## Priorité du répertoire courant +Priorité du répertoire courant +------------------------------ Prenons un exemple. Si vous ouvrez un explorateur de fichiers dans le deuxième -élément de la liste de `sys.path` (`/usr/lib/python3.8/` sur ma machine), vous trouverez +élément de la liste de ``sys.path`` (``/usr/lib/python3.8/`` sur ma machine), vous trouverez un grand nombre de fichiers Python. -Notamment, vous devriez trouver un fichier `random.py` dans ce répertoire. +notamment, vous devriez trouver un fichier ``random.py`` dans ce répertoire. En fait, vous trouverez la plupart des modules de la bibliothèque standard dans ce répertoire. -Maintenant, imaginons que vous avez un deuxième fichier `random.py` dans votre répertoire courant. Finalement, imaginez -que vous lancez un fichier `foo.py` contentant `import random` dans ce même réportoire. +Maintenant, imaginons que vous avez un deuxième fichier ``random.py`` dans votre répertoire courant. Finalement, imaginez +que vous lancez un fichier ``foo.py`` contentant ``import random`` dans ce même réportoire. -Et bien, c'est le fichier `random.py` de votre répertoire qui sera utilisé, et non celui de la bibliothèque standard! +Et bien, c'est le fichier ``random.py`` de votre répertoire qui sera utilisé, et non celui de la bibliothèque standard! -## Permissions des répertoires de sys.path +Permissions des répertoires de sys.path +--------------------------------------- -Un autre aspect notable de `sys.path` est qu'il ne contient que deux répertoires dans lesquels l'utilisateur courant peut potentiellement écrire : le chemin courant et le chemin dans `~/.local/lib`. Tous les autres (`/usr/lib/python3.8/`, etc.) sont des chemins "système" et ne peuvent être modifiés que par un compte administrateur (avec `root` ou `sudo`, donc). +Un autre aspect notable de ``sys.path`` est qu'il ne contient que deux +répertoires dans lesquels l'utilisateur courant peut potentiellement écrire +: le chemin courant et le chemin dans ``~/.local/lib``. Tous les autres +(``/usr/lib/python3.8/``, etc.) sont des chemins "système" et ne peuvent +être modifiés que par un compte administrateur (avec ``root`` ou ``sudo``, donc). -La situation est semblable sur macOS et Windows [^2]. - -## Bibliothèques tierces - -Prenons un exemple : - -```python -# dans foo.py -import tabulate - -scores = [ - ["John", 345], - ["Mary-Jane", 2], - ["Bob", 543], -] -table = tabulate.tabulate(scores) -print(table) -``` - -``` -$ python3 foo.py ---------- --- -John 345 -Mary-Jane 2 -Bob 543 ---------- --- -``` - -Ici, le module `tabulate` n'est ni dans la bibliothèque standard, ni écrit par l'auteur du script `foo.py`. On dit que c'est une bibliothèque tierce. - -On peut trouver [le code source de tabulate](https://bitbucket.org/astanin/python-tabulate/src/master/) facilement. La question qui se pose alors est: comment faire en sorte que `sys.path` contienne le module `tabulate`? - -Eh bien, plusieurs solutions s'offrent à vous. - -# Le gestionnaire de paquets - -Si vous utilisez une distribution Linux, peut-être pourrez-vous utiliser votre gestionnaire de paquets : - -```bash -$ sudo apt install python3-tabulate -``` - -Comme vous lancez votre gestionnaire de paquets avec `sudo`, celui-ci sera capable d'écrire dans les chemins système de `sys.path`. - -# À la main - -Une autre méthode consiste à partir des sources - par exemple, si le paquet de votre distribution n'est pas assez récent, ou si vous avez besoin de modifier le code de la bibliothèque en question. - -Voici une marche à suivre possible : - -1. Récupérer les sources de la version qui vous intéresse dans la [section téléchargement de bitbucket](https://bitbucket.org/astanin/python-tabulate/downloads/?tab=tags). -1. Extraire l'archive, par exemple dans `src/tabulate` -1. Se rendre dans `src/tabulate` et lancer `python3 setup.py install --user` - -# Anatomie du fichier setup.py - -La plupart des bibliothèques Python contiennent un `setup.py` à -la racine de leurs sources. Il sert à plein de choses, la commande `install` -n'étant qu'une parmi d'autres. - - -Le fichier `setup.py` contient en général simplement un `import` de `setuptools`, et un appel à la fonction `setup()`, avec de nombreux arguments : - -```python -# tabulate/setup.py -from setuptools import setup - -setup( - name='tabulate', - version='0.8.1', - description='Pretty-print tabular data', - py_modules=["tabulate"], - scripts=["bin/tabulate"], - ... -) -``` - - -# Résultat de l'invocation de setup.py - - -Par défaut, `setup.py` essaiera d'écrire dans un des chemins système de -`sys.path` [^3], d'où l'utilisation de l'option `--user`. - -Voici à quoi ressemble la sortie de la commande : - -```bash -$ cd src/tabulate -$ python3 setup.py install --user -running install -... -Copying tabulate-0.8.4-py3.7.egg to /home/dmerej/.local/lib/python3.7/site-packages -... -Installing tabulate script to /home/dmerej/.local/bin -``` - - -Notez que module a été copié dans `~/.local/lib/python3.7/site-packages/` et le script dans `~/.local/bin`. Cela signifie que *tous* les scripts Python lancés par l'utilisateur courant auront accès au module `tabulate`. - -Notez également qu'un script a été installé dans `~/.local/bin` - Une bibliothèque Python peut contenir aussi bien des modules que des scripts. - -Un point important est que vous n'avez en général pas besoin de lancer le script directement. Vous pouvez utiliser `python3 -m tabulate`. Procéder de cette façon est intéressant puisque vous n'avez pas à vous soucier de rajouter le chemin d'installation des scripts dans la variable d'environnement PATH. - - -# Dépendances - -Prenons une autre bibliothèque : `cli-ui`. - -Elle permet d'afficher du texte en couleur dans un terminal - -```python -import cli_ui - -cli_ui.info("Ceci est en", cli_ui.red, "rouge") -``` - -Elle permet également d'afficher des tableaux en couleur : - -```python -headers=["name", "score"] -data = [ - [(bold, "John"), (green, 10.0)], - [(bold, "Jane"), (green, 5.0)], -] -cli_ui.info_table(data, headers=headers) -``` - -Pour ce faire, elle repose sur la bibliothèque `tabulate` vue précédemment. On dit que `cli-ui` *dépend* de `tabulate`. - -# Déclaration des dépendances - -La déclaration de la dépendance de `cli-ui` vers `tabulate` s'effectue également dans le fichier `setup.py`: - -```python -setup( - name="cli-ui", - version="0.9.1", - install_requires=[ - "tabulate", - ... - ], - ... -) -``` - -# pypi.org - -On comprend dès lors qu'il doit nécessairement exister un *annuaire* permettant de relier les noms de dépendances à leur code source. - -Cet annuaire, c'est le site [pypi.org](https://pypi.org/). Vous y trouverez les pages correspondant à [tabulate](https://pypi.org/project/tabulate/) et [cli-ui](https://pypi.org/project/python-cli-ui/). - -# pip - -`pip` est un outil qui vient par défaut avec Python3[^4]. Vous pouvez également l'installer grâce au script [get-pip.py](https://bootstrap.pypa.io/get-pip.py), en lançant `python3 get-pip.py --user`. - -Il est conseillé de *toujours* lancer `pip` avec `python3 -m pip`. De cette façon, vous êtes certains d'utiliser le module `pip` correspondant à votre binaire `python3`, et vous ne dépendez pas de ce qu'il y a dans votre `PATH`. - -`pip` est capable d'interroger le site `pypi.org` pour retrouver les dépendances, et également de lancer les différents scripts `setup.py`. - -Comme de nombreux outils, il s'utilise à l'aide de *commandes*. Voici comment installer `cli-ui` à l'aide de la commande 'install' de `pip`: - -```bash -$ python3 -m pip install cli-ui --user -Collecting cli-ui -... -Requirement already satisfied: unidecode in /usr/lib/python3.7/site-packages (from cli-ui) (1.0.23) -Requirement already satisfied: colorama in /usr/lib/python3.7/site-packages (from cli-ui) (0.4.1) -Requirement already satisfied: tabulate in /mnt/data/dmerej/src/python-tabulate (from cli-ui) (0.8.4) -Installing collected packages: cli-ui -Successfully installed cli-ui-0.9.1 -``` - -On constate ici quelques limitations de `pip`: - -* Il faut penser à utiliser `--user` (de la même façon que lorsqu'on lance `setup.py` à la main) -* Si le paquet est déjà installé dans le système, pip ne saura pas le mettre à jour - il faudra passer par le gestionnaire de paquet de - la distribution - -En revanche, `pip` contient de nombreuses fonctionnalités intéressantes: - -* Il est capable de désinstaller des bibliothèques (à condition toutefois qu'elles ne soient pas dans un répertoire système) -* Il est aussi capable d'afficher la liste complète des bibliothèques Python accessibles par l'utilisateur courant avec `freeze`. - -Voici un extrait de la commande `python3 -m pip freeze` au moment de la rédaction de cet article sur ma machine: - -``` -$ python3 -m pip freeze -apipkg==1.5 -cli-ui==0.9.1 -gaupol==1.5 -tabulate==0.8.4 -``` - -On y retrouve les bibliothèques `cli-ui` et `tabulate`, bien sûr, mais aussi la bibliothèque `gaupol`, qui correspond au [programme d'édition de sous-titres](https://otsaloma.io/gaupol/) que j'ai installé à l'aide du gestionnaire de paquets de ma distribution. Précisons que les modules de la bibliothèque standard et ceux utilisés directement par pip sont omis de la liste. - -On constate également que chaque bibliothèque possède un *numéro de version*. - -# Numéros de version - -Les numéros de version remplissent plusieurs rôles, mais l'un des principaux est de spécifier des changements incompatibles. - -Par exemple, pour `cli-ui`, la façon d'appeler la fonction `ask_choice` a changé entre les versions 0.7 et 0.8, comme le montre cet extrait du [changelog](https://tankerhq.github.io/python-cli-ui/changelog.html#v0-8-0): - -> the list of choices used by ask_choice is now a named keyword argument: - -```python -# Old (<= 0.7) -ask_choice("select a fruit", ["apple", "banana"]) -# New (>= 0.8) -ask_choice("select a fruit", choices=["apple", "banana"]) -``` - -Ceci s'appelle un *changement d'API*. - -# Réagir aux changements d'API - -Plusieurs possibilités: - -* On peut bien sûr adapter le code pour utiliser la nouvelle API, mais cela n'est pas toujours possible ni souhaitable. -* Une autre solution est de spécifier des *contraintes* sur le numéro de version dans la déclaration des dépendances. Par exemple : - -```python -setup( - install_requires=[ - "cli-ui < 0.8", - ... - ] -) -``` - -# Aparté : pourquoi éviter sudo pip - -Souvenez-vous que les fichiers systèmes sont contrôlés par votre gestionnaire de paquets. - -Les mainteneurs de votre distribution font en sorte qu'ils fonctionnent bien les uns -avec les autres. Par exemple, le paquet `python3-cli-ui` ne sera mis à jour que lorsque tous les paquets qui en dépendent seront prêts à utiliser la nouvelle API. - -En revanche, si vous lancez `sudo pip` (où `pip` avec un compte root), vous allez écrire dans ces mêmes répertoire et vous risquez de "casser" certains programmes de votre système. - -Mais il y a un autre problème encore pire. - -# Conflit de dépendances - -Supposons deux projets A et B dans votre répertoire personnel. Ils dépendent tous les deux de `cli-ui`, mais l'un des deux utilise `cli-ui 0.7` et l'autre `cli-ui 0.9`. Que faire ? - -# Environnements virtuels - -La solution est d'utiliser un environnement virtuel (*virtualenv* en abrégé). C'est un répertoire *isolé* du reste du système. - -Il se crée par exemple avec la commande `python3 -m venv foo-venv`. où `foo-venv` est un répertoire quelconque. - -## Aparté : python3 -m venv sur Debian - -La commande `python3 -m venv` fonctionne en général partout, dès l'installation de Python3 (*out of the box*, en Anglais), *sauf* sur Debian et ses dérivées [^5]. - -Si vous utilisez Debian, la commande pourrait ne pas fonctionner. En fonction des messages d'erreur que vous obtenez, il est possible de résoudre le problème en : - -* installant le paquet `python3-venv`, -* ou en utilisant d'abord `pip` pour installer `virtualenv`, avec `python3 -m pip install virtualenv --user` puis en lançant `python3 -m virtualenv foo-venv`. - -## Comportement de python dans le virtualenv - -Ce répertoire contient de nombreux fichiers et dossiers, et notamment un binaire dans `foo-venv/bin/python3`. - -Voyons comment il se comporte en le comparant au binaire `/usr/bin/python3` habituel : - -``` -$ /usr/bin/python3 -c 'import sys; print(sys.path)' -['', - ... - '/usr/lib/python3.7', - '/usr/lib/python3.7.zip', - '/usr/lib/python3.7/lib-dynload', - '/home/dmerej/.local/lib/python3.7/site-packages', - '/usr/lib/python3.7/site-packages' -] - -$ /home/dmerej/foo-venv/bin/python -c 'import sys; print(sys.path)' -['', - '/usr/lib/python3.7', - '/usr/lib/python3.7.zip', - '/usr/lib/python3.7/lib-dynload', - '/home/dmerej/foo-venv/lib/python3.7/site-packages, -] -``` - -À noter: - -* Le répertoire "global" dans `~/.local/lib` a disparu -* Seuls quelques répertoires systèmes sont présents (ils correspondent plus ou moins à l'emplacement des modules de la bibliothèque standard) -* Un répertoire *au sein* du virtualenv a été rajouté - -Ainsi, l'isolation du virtualenv est reflété dans la différence de la valeur de `sys.path`. - -Il faut aussi préciser que le virtualenv n'est pas complètement isolé du reste du système. En particulier, il dépend encore du binaire Python utilisé pour le créer. - -Par exemple, si vous utilisez `/usr/local/bin/python3.7 -m venv foo-37`, le virtualenv dans `foo-37` utilisera Python 3.7 et fonctionnera tant que le binaire `/usr/local/bin/python3.7` existe. - -Cela signifie également qu'il est possible qu'en mettant à jour le paquet `python3` sur votre distribution, vous rendiez inutilisables les virtualenvs créés avec l'ancienne version du paquet. - - -## Comportement de pip dans le virtualenv - -D'après ce qui précède, le virtualenv ne devrait contenir aucun module en dehors de la bibliothèque standard et de `pip` lui-même. - -On peut s'en assurer en lançant `python3 -m pip freeze` depuis le virtualenv et en vérifiant que rien ne s'affiche. - -``` -$ python3 -m pip freeze -# de nombreuses bibliothèques en dehors du virtualenv -apipkg==1.5 -cli-ui==0.9.1 -gaupol==1.5 -tabulate==0.8.4 - -$ /home/dmerej/foo-venv/bin/python3 -m pip freeze -# rien :) -``` - -On peut alors utiliser le module `pip` *du virtualenv* pour installer des bibliothèques dans celui-ci : - -``` -$ /home/dmerej/foo-venv/bin/python3 -m pip install cli-ui -Collecting cli-ui - Using cached https://pythonhosted.org/..cli_ui-0.9.1-py3-none-any.whl -Collecting colorama (from cli-ui) - Using cached https://pythonhosted.org/..colorama-0.4.1-py2.py3-none-any.whl -Collecting unidecode (from cli-ui) - Using cached https://pythonhosted.org/..Unidecode-1.0.23-py2.py3-none-any.whl -Collecting tabulate (from cli-ui) -Installing collected packages: colorama, unidecode, tabulate, cli-ui -Successfully installed cli-ui-0.9.1 colorama-0.4.1 tabulate-0.8.3 - unidecode-1.0.23 -``` - -Cette fois, aucune bibliothèque n'est marquée comme déjà installée, et on récupère donc `cli-ui` et toutes ses dépendances. - -On a enfin notre solution pour résoudre notre conflit de dépendances : -on peut simplement créer un virtualenv par projet. Ceci nous permettra -d'avoir effectivement deux versions différentes de `cli-ui`, isolées les -unes des autres. - -# Activer un virtualenv - -Devoir préciser le chemin du virtualenv en entier pour chaque commande peut devenir fastidieux ; heureusement, il est possible *d'activer* un virtualenv, en lançant une des commandes suivantes : - -* `source foo-venv/bin/activate` - si vous utilisez un shell POSIX -* `source foo-venv/bin/activate.fish` - si vous utilisez Fish -* `foo-venv\bin\activate.bat` - sous Windows - -Une fois le virtualenv activé, taper `python`, `python3` ou `pip` utilisera les binaires correspondants dans le virtualenv automatiquement, -et ce, tant que la session du shell sera ouverte. - -Le script d'activation ne fait en réalité pas grand-chose à part modifier la variable `PATH` et rajouter le nom du virtualenv au début de l'invite de commandes : - -``` -# Avant -user@host:~/src $ source foo-env/bin/activate -# Après -(foo-env) user@host:~/src $ -``` - -Pour sortir du virtualenv, entrez la commande `deactivate`. - -# Conclusion - -Le système de gestions des dépendances de Python peut paraître compliqué et bizarre, surtout venant d'autres langages. - -Mon conseil est de toujours suivre ces deux règles : - -* Un virtualenv par projet et par version de Python -* Toujours utiliser `pip` *depuis* un virtualenv - -Certes, cela peut paraître fastidieux, mais c'est une méthode qui vous évitera probablement de vous arracher les cheveux (croyez-en mon expérience). - -Dans un futur article, nous approfondirons la question, en évoquant d'autres sujets comme `PYTHONPATH`, le fichier `requirements.txt` ou des outils comme `poetry` ou `pipenv`. À suivre. - - -[^1]: C'est une condition suffisante, mais pas nécessaire - on y reviendra. -[^2]: Presque. Il peut arriver que l'utilisateur courant ait les droits d'écriture dans *tous* les segments de `sys.path`, en fonction de l'installation de Python. Cela dit, c'est plutôt l'exception que la règle. -[^3]: Cela peut vous paraître étrange à première vue. Il y a de nombreuses raisons historiques à ce comportement, et il n'est pas sûr qu'il puisse être changé un jour. -[^4]: Presque. Parfois il faut installer un paquet supplémentaire, notamment sur les distributions basées sur Debian -[^5]: Je n'ai pas réussi à trouver une explication satisfaisante à ce choix des mainteneurs Debian. Si vous avez des informations à ce sujet, je suis preneur. _Mise à jour: Il se trouve que cette décision s'inscrit au sein de la "debian policy", c'est à dire une liste de règles que doivent respecter tous les programmes maintenus par Debian._ +La situation est semblable sur macOS et Windows. diff --git a/cours/source/14-bibliothèques-01/03-bibliotheques-tierces.rst b/cours/source/14-bibliothèques-01/03-bibliotheques-tierces.rst index 006f777..a690d4e 100644 --- a/cours/source/14-bibliothèques-01/03-bibliotheques-tierces.rst +++ b/cours/source/14-bibliothèques-01/03-bibliotheques-tierces.rst @@ -1,106 +1,280 @@ -+++ -title = "Bibliothèques tierces" -weight = 3 -+++ +Bibliothèques tierces +===================== -# Bibliothèques tierces +Prenons un exemple:: -Prenons un exemple : + # dans foo.py + import tabulate -```python -# dans foo.py -import tabulate + scores = [ + ["John", 345], + ["Mary-Jane", 2], + ["Bob", 543], + ] + table = tabulate.tabulate(scores) + print(table) -scores = [ - ["John", 345], - ["Mary-Jane", 2], - ["Bob", 543], -] -table = tabulate.tabulate(scores) -print(table) -``` +.. code-block:: console -``` -$ python3 foo.py ---------- --- -John 345 -Mary-Jane 2 -Bob 543 ---------- --- -``` + $ python3 foo.py + --------- --- + John 345 + Mary-Jane 2 + Bob 543 + --------- --- -Ici, le module `tabulate` n'est ni dans la bibliothèque standard, ni écrit par l'auteur du script `foo.py`. On dit que c'est une bibliothèque tierce. +Ici, le module ``tabulate`` n'est ni dans la bibliothèque standard, ni écrit par l'auteur du script ``foo.py``. On dit que c'est une bibliothèque tierce. -On peut trouver [le code source de tabulate](https://bitbucket.org/astanin/python-tabulate/src/master/) facilement. La question qui se pose alors est: comment faire en sorte que `sys.path` contienne le module `tabulate`? +On peut trouver `le code source de tabulate +`_ facilement. La +question qui se pose alors est: comment faire en sorte que `sys.path` +contienne le module ``tabulate``? Eh bien, plusieurs solutions s'offrent à vous. -## Le gestionnaire de paquets +Le gestionnaire de paquets +--------------------------- -Si vous utilisez une distribution Linux, peut-être pourrez-vous utiliser votre gestionnaire de paquets : +Si vous utilisez une distribution Linux, peut-être pourrez-vous utiliser votre gestionnaire de paquets: -```bash -$ sudo apt install python3-tabulate -``` +.. code-block:: console -Comme vous lancez votre gestionnaire de paquets avec `sudo`, celui-ci sera capable d'écrire dans les chemins système de `sys.path`. + $ sudo apt install python3-tabulate -## À la main +Comme vous lancez votre gestionnaire de paquets avec ``sudo``, celui-ci sera capable d'écrire dans les chemins système de ``sys.path``. + +À la main +---------- Une autre méthode consiste à partir des sources - par exemple, si le paquet de votre distribution n'est pas assez récent, ou si vous avez besoin de modifier le code de la bibliothèque en question. Voici une marche à suivre possible : -1. Récupérer les sources de la version qui vous intéresse dans la [section téléchargement de bitbucket](https://bitbucket.org/astanin/python-tabulate/downloads/?tab=tags). -1. Extraire l'archive, par exemple dans `src/tabulate` -1. Se rendre dans `src/tabulate` et lancer `python3 setup.py install --user` +1. Récupérer les sources de la version qui vous intéresse dans la `section téléchargement de bitbucket `_. +1. Extraire l'archive, par exemple dans ``src/tabulate`` +1. Se rendre dans ``src/tabulate`` et lancer ``python3 setup.py install --user`` -## Anatomie du fichier setup.py +Anatomie du fichier setup.py +----------------------------- -La plupart des bibliothèques Python contiennent un `setup.py` à -la racine de leurs sources. Il sert à plein de choses, la commande `install` +La plupart des bibliothèques Python contiennent un ``setup.py`` à +la racine de leurs sources. Il sert à plein de choses, la commande ``install`` n'étant qu'une parmi d'autres. -Le fichier `setup.py` contient en général simplement un `import` de `setuptools`, et un appel à la fonction `setup()`, avec de nombreux arguments : +Le fichier ``setup.py`` contient en général simplement un ``import`` de +``setuptools``, et un appel à la fonction ``setup()``, avec de nombreux +arguments:: + + # tabulate/setup.py + from setuptools import setup + + setup( + name='tabulate', + version='0.8.1', + description='Pretty-print tabular data', + py_modules=["tabulate"], + scripts=["bin/tabulate"], + ... + ) + + +Résultat de l'invocation de setup.py +------------------------------------- + +Par défaut, ``setup.py`` essaiera d'écrire dans un des chemins système de +``sys.path``, d'où l'utilisation de l'option ``--user``. + +Voici à quoi ressemble la sortie de la commande: + +.. code-block:: console + + $ cd src/tabulate + $ python3 setup.py install --user + running install + ... + Copying tabulate-0.8.4-py3.7.egg to /home/dmerej/.local/lib/python3.7/site-packages + ... + Installing tabulate script to /home/dmerej/.local/bin + +Notez que module a été copié dans ``~/.local/lib/python3.7/site-packages/`` +et le script dans ``~/.local/bin``. Cela signifie que *tous* les scripts Python +lancés par l'utilisateur courant auront accès au module ``tabulate``. + +Notez également qu'un script a été installé dans ``~/.local/bin`` - Une +bibliothèque Python peut contenir aussi bien des modules que des scripts. + +Un point important est que vous n'avez en général pas besoin de lancer le +script directement. Vous pouvez utiliser ``python3 -m tabulate``. Procéder +de cette façon est intéressant puisque vous n'avez pas à vous soucier de +rajouter le chemin d'installation des scripts dans la variable d'environnement +PATH. + + +Dépendances +----------- + +Prenons une autre bibliothèque : ``cli-ui``. + +Elle permet d'afficher du texte en couleur dans un terminal:: + + import cli_ui + + cli_ui.info("Ceci est en", cli_ui.red, "rouge") + +Elle permet également d'afficher des tableaux en couleur:: + + headers=["name", "score"] + data = [ + [(bold, "John"), (green, 10.0)], + [(bold, "Jane"), (green, 5.0)], + ] + cli_ui.info_table(data, headers=headers) + +Pour ce faire, elle repose sur la bibliothèque ``tabulate`` vue +précédemment. On dit que ``cli-ui`` *dépend* de ``tabulate``. + +Déclaration des dépendances +---------------------------- + +La déclaration de la dépendance de ``cli-ui`` vers ``tabulate`` s'effectue également dans le fichier ``setup.py``:: + + setup( + name="cli-ui", + version="0.9.1", + install_requires=[ + "tabulate", + ... + ], + ... + ) + +pypi.org +--------- + +On comprend dès lors qu'il doit nécessairement exister un *annuaire* permettant de relier les noms de dépendances à leur code source. + +Cet annuaire, c'est le site `pypi.org `_. Vous y trouverez +les pages correspondant à `tabulate `_ +et `cli-ui `_. + +pip +--- + +``pip`` est un outil qui vient par défaut avec Python3[^4]. Vous pouvez également l'installer grâce au script `get-pip.py `_, en lançant ``python3 get-pip.py --user``. + +Il est conseillé de *toujours* lancer ``pip`` avec ``python3 -m pip``. De cette +façon, vous êtes certains d'utiliser le module ``pip`` correspondant à votre +binaire ``python3``, et vous ne dépendez pas de ce qu'il y a dans votre ``PATH``. + +``pip`` est capable d'interroger le site ``pypi.org`` pour retrouver les +dépendances, et également de lancer les différents scripts ``setup.py``. + +Comme de nombreux outils, il s'utilise à l'aide de *commandes*. Voici +comment installer ``cli-ui`` à l'aide de la commande 'install' de ``pip``: + +.. code-block:: console + + $ python3 -m pip install cli-ui --user + Collecting cli-ui + ... + Requirement already satisfied: unidecode in /usr/lib/python3.7/site-packages (from cli-ui) (1.0.23) + Requirement already satisfied: colorama in /usr/lib/python3.7/site-packages (from cli-ui) (0.4.1) + Requirement already satisfied: tabulate in /mnt/data/dmerej/src/python-tabulate (from cli-ui) (0.8.4) + Installing collected packages: cli-ui + Successfully installed cli-ui-0.9.1 + +On constate ici quelques limitations de ``pip``: + +* Il faut penser à utiliser ``--user`` (de la même façon que lorsqu'on lance ``setup.py`` à la main) +* Si le paquet est déjà installé dans le système, pip ne saura pas le +mettre à jour - il faudra passer par le gestionnaire de paquet de + la distribution + +En revanche, `pip` contient de nombreuses fonctionnalités intéressantes: + +* Il est capable de désinstaller des bibliothèques (à condition toutefois + qu'elles ne soient pas dans un répertoire système) +* Il est aussi capable d'afficher la liste complète des bibliothèques + Python accessibles par l'utilisateur courant avec `freeze`. + +Voici un extrait de la commande ``python3 -m pip freeze`` au moment de la rédaction de cet article sur ma machine: + +.. code-block:: console + + $ python3 -m pip freeze + apipkg==1.5 + cli-ui==0.9.1 + gaupol==1.5 + tabulate==0.8.4 + +On y retrouve les bibliothèques ``cli-ui`` et ``tabulate``, bien sûr, mais +aussi la bibliothèque ``gaupol``, qui correspond au [programme d'édition de +sous-titres](https://otsaloma.io/gaupol/) que j'ai installé à l'aide du +gestionnaire de paquets de ma distribution. Précisons que les modules de +la bibliothèque standard et ceux utilisés directement par pip sont omis +de la liste. + +On constate également que chaque bibliothèque possède un *numéro de version*. + +Numéros de version +------------------- + +Les numéros de version remplissent plusieurs rôles, mais l'un des principaux +est de spécifier des changements incompatibles. + +Par exemple, pour ``cli-ui``, la façon d'appeler la fonction ``ask_choice`` +a changé entre les versions 0.7 et 0.8, comme le montre cet extrait du +`changelog `_: + + *The list of choices used by ask_choice is now a named keyword argument:* + + .. code-block:: + + # Old (<= 0.7) + ask_choice("select a fruit", ["apple", "banana"]) + # New (>= 0.8) + ask_choice("select a fruit", choices=["apple", "banana"]) -```python -# tabulate/setup.py -from setuptools import setup +Ceci s'appelle un *changement d'API*. -setup( - name='tabulate', - version='0.8.1', - description='Pretty-print tabular data', - py_modules=["tabulate"], - scripts=["bin/tabulate"], - ... -) -``` +Réagir aux changements d'API +----------------------------- +Plusieurs possibilités: -## Résultat de l'invocation de setup.py +* On peut bien sûr adapter le code pour utiliser la nouvelle API, mais cela + n'est pas toujours possible ni souhaitable. +* Une autre solution est de spécifier des *contraintes* sur le numéro de + version dans la déclaration des dépendances. Par exemple:: + setup( + install_requires=[ + "cli-ui < 0.8", + ... + ] + ) -Par défaut, `setup.py` essaiera d'écrire dans un des chemins système de -`sys.path` [^3], d'où l'utilisation de l'option `--user`. +Aparté : pourquoi éviter sudo pip +--------------------------------- -Voici à quoi ressemble la sortie de la commande : +Souvenez-vous que les fichiers systèmes sont contrôlés par votre gestionnaire de paquets. -```bash -$ cd src/tabulate -$ python3 setup.py install --user -running install -... -Copying tabulate-0.8.4-py3.7.egg to /home/dmerej/.local/lib/python3.7/site-packages -... -Installing tabulate script to /home/dmerej/.local/bin -``` +Les mainteneurs de votre distribution font en sorte qu'ils fonctionnent bien les uns +avec les autres. Par exemple, le paquet ``python3-cli-ui`` ne sera mis à jour +que lorsque tous les paquets qui en dépendent seront prêts à utiliser la +nouvelle API. +En revanche, si vous lancez ``sudo pip`` (où ``pip`` avec un compte root), +vous allez écrire dans ces mêmes répertoire et vous risquez de "casser" +certains programmes de votre système. -Notez que module a été copié dans `~/.local/lib/python3.7/site-packages/` et le script dans `~/.local/bin`. Cela signifie que *tous* les scripts Python lancés par l'utilisateur courant auront accès au module `tabulate`. +Mais il y a un autre problème encore pire. -Notez également qu'un script a été installé dans `~/.local/bin` - Une bibliothèque Python peut contenir aussi bien des modules que des scripts. +Conflit de dépendances +---------------------- -Un point important est que vous n'avez en général pas besoin de lancer le script directement. Vous pouvez utiliser `python3 -m tabulate`. Procéder de cette façon est intéressant puisque vous n'avez pas à vous soucier de rajouter le chemin d'installation des scripts dans la variable d'environnement PATH. +Supposons deux projets A et B dans votre répertoire personnel. Ils dépendent +tous les deux de ``cli-ui``, mais l'un des deux utilise ``cli-ui 0.7`` et l'autre +``cli-ui 0.9``. Que faire ? diff --git a/cours/source/14-bibliothèques-01/04-dépendances.rst b/cours/source/14-bibliothèques-01/04-dépendances.rst index 782c9a7..ad4c85b 100644 --- a/cours/source/14-bibliothèques-01/04-dépendances.rst +++ b/cours/source/14-bibliothèques-01/04-dépendances.rst @@ -1,147 +1,166 @@ -+++ -title = "Dépendances" -weight = 4 -+++ +Dépendances +=========== -# Dépendances +Prenons une autre bibliothèque : ``cli-ui``. -## Un autre exemple +Elle permet d'afficher du texte en couleur dans un terminal:: -Prenons une autre bibliothèque : `cli-ui`. + import cli_ui -Elle permet d'afficher du texte en couleur dans un terminal + cli_ui.info("Ceci est en", cli_ui.red, "rouge") -```python -import cli_ui +Elle permet également d'afficher des tableaux en couleur:: -cli_ui.info("Ceci est en", cli_ui.red, "rouge") -``` + headers=["name", "score"] + data = [ + [(bold, "John"), (green, 10.0)], + [(bold, "Jane"), (green, 5.0)], + ] + cli_ui.info_table(data, headers=headers) -Elle permet également d'afficher des tableaux en couleur : +Pour ce faire, elle repose sur la bibliothèque ``tabulate`` vue +précédemment. On dit que ``cli-ui`` *dépend* de ``tabulate``. -```python -headers=["prénom", "score"] -data = [ - [(bold, "John"), (green, 10.0)], - [(bold, "Jane"), (green, 5.0)], -] -cli_ui.info_table(data, headers=headers) -``` +Déclaration des dépendances +---------------------------- -Pour ce faire, elle repose sur la bibliothèque `tabulate` vue précédemment. On dit que `cli-ui` *dépend* de `tabulate`. +La déclaration de la dépendance de ``cli-ui`` vers ``tabulate`` s'effectue également dans le fichier ``setup.py``:: -## Déclaration des dépendances + setup( + name="cli-ui", + version="0.9.1", + install_requires=[ + "tabulate", + ... + ], + ... + ) -La déclaration de la dépendance de `cli-ui` vers `tabulate` s'effectue également dans le fichier `setup.py`: - -```python -setup( - name="cli-ui", - version="0.9.1", - install_requires=[ - "tabulate", - ... - ], - ... -) -``` - -## pypi.org +pypi.org +--------- On comprend dès lors qu'il doit nécessairement exister un *annuaire* permettant de relier les noms de dépendances à leur code source. -Cet annuaire, c'est le site [pypi.org](https://pypi.org/). Vous y trouverez les pages correspondant à [tabulate](https://pypi.org/project/tabulate/) et [cli-ui](https://pypi.org/project/python-cli-ui/). +Cet annuaire, c'est le site `pypi.org `_. Vous y trouverez +les pages correspondant à `tabulate `_ +et `cli-ui `_. + +pip +--- -# pip +``pip`` est un outil qui vient par défaut avec Python3[^4]. Vous pouvez également l'installer grâce au script `get-pip.py `_, en lançant ``python3 get-pip.py --user``. -`pip` est un outil qui vient par défaut avec Python3[^4]. Vous pouvez également l'installer grâce au script [get-pip.py](https://bootstrap.pypa.io/get-pip.py), en lançant `python3 get-pip.py --user`. +Il est conseillé de *toujours* lancer ``pip`` avec ``python3 -m pip``. De cette +façon, vous êtes certains d'utiliser le module ``pip`` correspondant à votre +binaire ``python3``, et vous ne dépendez pas de ce qu'il y a dans votre ``PATH``. -Il est conseillé de *toujours* lancer `pip` avec `python3 -m pip`. De cette façon, vous êtes certains d'utiliser le module `pip` correspondant à votre binaire `python3`, et vous ne dépendez pas de ce qu'il y a dans votre `PATH`. +``pip`` est capable d'interroger le site ``pypi.org`` pour retrouver les +dépendances, et également de lancer les différents scripts ``setup.py``. -`pip` est capable d'interroger le site `pypi.org` pour retrouver les dépendances, et également de lancer les différents scripts `setup.py`. +Comme de nombreux outils, il s'utilise à l'aide de *commandes*. Voici +comment installer ``cli-ui`` à l'aide de la commande 'install' de ``pip``: -Comme de nombreux outils, il s'utilise à l'aide de *commandes*. Voici comment installer `cli-ui` à l'aide de la commande 'install' de `pip`: +.. code-block:: console -```bash -$ python3 -m pip install cli-ui --user -Collecting cli-ui -... -Requirement already satisfied: unidecode in /usr/lib/python3.7/site-packages (from cli-ui) (1.0.23) -Requirement already satisfied: colorama in /usr/lib/python3.7/site-packages (from cli-ui) (0.4.1) -Requirement already satisfied: tabulate in /mnt/data/dmerej/src/python-tabulate (from cli-ui) (0.8.4) -Installing collected packages: cli-ui -Successfully installed cli-ui-0.9.1 -``` + $ python3 -m pip install cli-ui --user + Collecting cli-ui + ... + Requirement already satisfied: unidecode in /usr/lib/python3.7/site-packages (from cli-ui) (1.0.23) + Requirement already satisfied: colorama in /usr/lib/python3.7/site-packages (from cli-ui) (0.4.1) + Requirement already satisfied: tabulate in /mnt/data/dmerej/src/python-tabulate (from cli-ui) (0.8.4) + Installing collected packages: cli-ui + Successfully installed cli-ui-0.9.1 -On constate ici quelques limitations de `pip`: +On constate ici quelques limitations de ``pip``: -* Il faut penser à utiliser `--user` (de la même façon que lorsqu'on lance `setup.py` à la main) -* Si le paquet est déjà installé dans le système, pip ne saura pas le mettre à jour - il faudra passer par le gestionnaire de paquet de +* Il faut penser à utiliser ``--user`` (de la même façon que lorsqu'on lance ``setup.py`` à la main) +* Si le paquet est déjà installé dans le système, pip ne saura pas le +mettre à jour - il faudra passer par le gestionnaire de paquet de la distribution En revanche, `pip` contient de nombreuses fonctionnalités intéressantes: -* Il est capable de désinstaller des bibliothèques (à condition toutefois qu'elles ne soient pas dans un répertoire système) -* Il est aussi capable d'afficher la liste complète des bibliothèques Python accessibles par l'utilisateur courant avec `freeze`. +* Il est capable de désinstaller des bibliothèques (à condition toutefois + qu'elles ne soient pas dans un répertoire système) +* Il est aussi capable d'afficher la liste complète des bibliothèques + Python accessibles par l'utilisateur courant avec `freeze`. -Voici un extrait de la commande `python3 -m pip freeze` au moment de la rédaction de cet article sur ma machine: +Voici un extrait de la commande ``python3 -m pip freeze`` au moment de la rédaction de cet article sur ma machine: -``` -$ python3 -m pip freeze -apipkg==1.5 -cli-ui==0.9.1 -gaupol==1.5 -tabulate==0.8.4 -``` +.. code-block:: console -On y retrouve les bibliothèques `cli-ui` et `tabulate`, bien sûr, mais aussi la bibliothèque `gaupol`, qui correspond au [programme d'édition de sous-titres](https://otsaloma.io/gaupol/) que j'ai installé à l'aide du gestionnaire de paquets de ma distribution. Précisons que les modules de la bibliothèque standard et ceux utilisés directement par pip sont omis de la liste. + $ python3 -m pip freeze + apipkg==1.5 + cli-ui==0.9.1 + gaupol==1.5 + tabulate==0.8.4 + +On y retrouve les bibliothèques ``cli-ui`` et ``tabulate``, bien sûr, mais +aussi la bibliothèque ``gaupol``, qui correspond au [programme d'édition de +sous-titres](https://otsaloma.io/gaupol/) que j'ai installé à l'aide du +gestionnaire de paquets de ma distribution. Précisons que les modules de +la bibliothèque standard et ceux utilisés directement par pip sont omis +de la liste. On constate également que chaque bibliothèque possède un *numéro de version*. -## Numéros de version +Numéros de version +------------------- + +Les numéros de version remplissent plusieurs rôles, mais l'un des principaux +est de spécifier des changements incompatibles. -Les numéros de version remplissent plusieurs rôles, mais l'un des principaux est de spécifier des changements incompatibles. +Par exemple, pour ``cli-ui``, la façon d'appeler la fonction ``ask_choice`` +a changé entre les versions 0.7 et 0.8, comme le montre cet extrait du +`changelog `_: -Par exemple, pour `cli-ui`, la façon d'appeler la fonction `ask_choice` a changé entre les versions 0.7 et 0.8, comme le montre cet extrait du [changelog](https://tankerhq.github.io/python-cli-ui/changelog.html#v0-8-0): + *The list of choices used by ask_choice is now a named keyword argument:* -> the list of choices used by ask_choice is now a named keyword argument: + .. code-block:: -```python -# Old (<= 0.7) -ask_choice("select a fruit", ["apple", "banana"]) -# New (>= 0.8) -ask_choice("select a fruit", choices=["apple", "banana"]) -``` + # Old (<= 0.7) + ask_choice("select a fruit", ["apple", "banana"]) + # New (>= 0.8) + ask_choice("select a fruit", choices=["apple", "banana"]) Ceci s'appelle un *changement d'API*. -## Réagir aux changements d'API +Réagir aux changements d'API +----------------------------- Plusieurs possibilités: -* On peut bien sûr adapter le code pour utiliser la nouvelle API, mais cela n'est pas toujours possible ni souhaitable. -* Une autre solution est de spécifier des *contraintes* sur le numéro de version dans la déclaration des dépendances. Par exemple : +* On peut bien sûr adapter le code pour utiliser la nouvelle API, mais cela + n'est pas toujours possible ni souhaitable. +* Une autre solution est de spécifier des *contraintes* sur le numéro de + version dans la déclaration des dépendances. Par exemple:: -```python -setup( - install_requires=[ - "cli-ui < 0.8", - ... - ] -) -``` + setup( + install_requires=[ + "cli-ui < 0.8", + ... + ] + ) -# Aparté : pourquoi éviter sudo pip +Aparté : pourquoi éviter sudo pip +--------------------------------- Souvenez-vous que les fichiers systèmes sont contrôlés par votre gestionnaire de paquets. Les mainteneurs de votre distribution font en sorte qu'ils fonctionnent bien les uns -avec les autres. Par exemple, le paquet `python3-cli-ui` ne sera mis à jour que lorsque tous les paquets qui en dépendent seront prêts à utiliser la nouvelle API. +avec les autres. Par exemple, le paquet ``python3-cli-ui`` ne sera mis à jour +que lorsque tous les paquets qui en dépendent seront prêts à utiliser la +nouvelle API. -En revanche, si vous lancez `sudo pip` (où `pip` avec un compte root), vous allez écrire dans ces mêmes répertoire et vous risquez de "casser" certains programmes de votre système. +En revanche, si vous lancez ``sudo pip`` (où ``pip`` avec un compte root), +vous allez écrire dans ces mêmes répertoire et vous risquez de "casser" +certains programmes de votre système. Mais il y a un autre problème encore pire. -# Conflit de dépendances +Conflit de dépendances +---------------------- -Supposons deux projets A et B dans votre répertoire personnel. Ils dépendent tous les deux de `cli-ui`, mais l'un des deux utilise `cli-ui 0.7` et l'autre `cli-ui 0.9`. Que faire ? +Supposons deux projets A et B dans votre répertoire personnel. Ils dépendent +tous les deux de ``cli-ui``, mais l'un des deux utilise ``cli-ui 0.7`` et l'autre +``cli-ui 0.9``. Que faire ? diff --git a/cours/source/14-bibliothèques-01/05-virtualenv.rst b/cours/source/14-bibliothèques-01/05-virtualenv.rst index 4a2949d..482b14c 100644 --- a/cours/source/14-bibliothèques-01/05-virtualenv.rst +++ b/cours/source/14-bibliothèques-01/05-virtualenv.rst @@ -1,137 +1,168 @@ -+++ -title = "Environnements virtuels" -weight = 5 -+++ +Environnements virtuels +======================== -# Environnements virtuels +La solution est d'utiliser un environnement virtuel (*virtualenv* en +abrégé). C'est un répertoire *isolé* du reste du système. -La solution à la question de la fin du chapitre précédent est d'utiliser un *environnement virtuel* -(*virtualenv* en abrégé). +Il se crée par exemple avec la commande ``python3 -m venv foo-venv``. où +``foo-venv`` est un répertoire quelconque. -C'est un répertoire *isolé* du reste du système. +Aparté : python3 -m venv sur Debian +------------------------------------ -Il se crée par exemple avec la commande `python3 -m venv foo-venv`. où `foo-venv` est un répertoire quelconque. +La commande `python3 -m venv` fonctionne en général partout, dès +l'installation de Python3 (*out of the box*, en Anglais), *sauf* sur Debian +et ses dérivées. -## Aparté : python3 -m venv sur Debian +Si vous utilisez Debian, la commande pourrait ne pas fonctionner. En fonction +des messages d'erreur que vous obtenez, il est possible de résoudre le +problème en : -La commande `python3 -m venv` fonctionne en général partout, dès l'installation de Python3 (*out of the box*, en Anglais), *sauf* sur Debian et ses dérivées [^5]. +* installant le paquet ``python3-venv``, +* ou en utilisant d'abord ``pip`` pour installer ``virtualenv``, avec +``python3 -m pip install virtualenv --user`` puis en lançant ``python3 -m +virtualenv foo-venv``. -Si vous utilisez Debian, la commande pourrait ne pas fonctionner. En fonction des messages d'erreur que vous obtenez, il est possible de résoudre le problème en : +Comportement de python dans le virtualenv +----------------------------------------- -* installant le paquet `python3-venv`, -* ou en utilisant d'abord `pip` pour installer `virtualenv`, avec `python3 -m pip install virtualenv --user` puis en lançant `python3 -m virtualenv foo-venv`. +Ce répertoire contient de nombreux fichiers et dossiers, et notamment un +binaire dans ``foo-venv/bin/python3``. -## Comportement de python dans le virtualenv +Voyons comment il se comporte en le comparant au binaire ``/usr/bin/python3`` +habituel: -Ce répertoire contient de nombreux fichiers et dossiers, et notamment un binaire dans `foo-venv/bin/python3`. +.. code-block:: console -Voyons comment il se comporte en le comparant au binaire `/usr/bin/python3` habituel : + $ /usr/bin/python3 -c 'import sys; print(sys.path)' + ['', + ... + '/usr/lib/python3.7', + '/usr/lib/python3.7.zip', + '/usr/lib/python3.7/lib-dynload', + '/home/dmerej/.local/lib/python3.7/site-packages', + '/usr/lib/python3.7/site-packages' + ] -``` -$ /usr/bin/python3 -c 'import sys; print(sys.path)' -['', - ... - '/usr/lib/python3.7', - '/usr/lib/python3.7.zip', - '/usr/lib/python3.7/lib-dynload', - '/home/dmerej/.local/lib/python3.7/site-packages', - '/usr/lib/python3.7/site-packages' -] +.. code-block:: console -$ /home/dmerej/foo-venv/bin/python -c 'import sys; print(sys.path)' -['', - '/usr/lib/python3.7', - '/usr/lib/python3.7.zip', - '/usr/lib/python3.7/lib-dynload', - '/home/dmerej/foo-venv/lib/python3.7/site-packages, -] -``` + $ /home/dmerej/foo-venv/bin/python -c 'import sys; print(sys.path)' + ['', + '/usr/lib/python3.7', + '/usr/lib/python3.7.zip', + '/usr/lib/python3.7/lib-dynload', + '/home/dmerej/foo-venv/lib/python3.7/site-packages, + ] À noter: -* Le répertoire "global" dans `~/.local/lib` a disparu -* Seuls quelques répertoires systèmes sont présents (ils correspondent plus ou moins à l'emplacement des modules de la bibliothèque standard) +* Le répertoire "global" dans ``~/.local/lib`` a disparu +* Seuls quelques répertoires systèmes sont présents (ils correspondent +plus ou moins à l'emplacement des modules de la bibliothèque standard) * Un répertoire *au sein* du virtualenv a été rajouté -Ainsi, l'isolation du virtualenv est reflété dans la différence de la valeur de `sys.path`. +Ainsi, l'isolation du virtualenv est reflété dans la différence de la +valeur de ``sys.path``. -Il faut aussi préciser que le virtualenv n'est pas complètement isolé du reste du système. En particulier, il dépend encore du binaire Python utilisé pour le créer. +Il faut aussi préciser que le virtualenv n'est pas complètement isolé +du reste du système. En particulier, il dépend encore du binaire Python +utilisé pour le créer. -Par exemple, si vous utilisez `/usr/local/bin/python3.7 -m venv foo-37`, le virtualenv dans `foo-37` utilisera Python 3.7 et fonctionnera tant que le binaire `/usr/local/bin/python3.7` existe. +Par exemple, si vous utilisez ``/usr/local/bin/python3.7 -m venv foo-37``, +le virtualenv dans ``foo-37`` utilisera Python 3.7 et fonctionnera tant que +le binaire ``/usr/local/bin/python3.7`` existe. -Cela signifie également qu'il est possible qu'en mettant à jour le paquet `python3` sur votre distribution, vous rendiez inutilisables les virtualenvs créés avec l'ancienne version du paquet. +Cela signifie également qu'il est possible qu'en mettant à jour le paquet +``python3`` sur votre distribution, vous rendiez inutilisables les virtualenvs +créés avec l'ancienne version du paquet. -## Comportement de pip dans le virtualenv +Comportement de pip dans le virtualenv +--------------------------------------- -D'après ce qui précède, le virtualenv ne devrait contenir aucun module en dehors de la bibliothèque standard et de `pip` lui-même. +D'après ce qui précède, le virtualenv ne devrait contenir aucun module +en dehors de la bibliothèque standard et de ``pip`` lui-même. -On peut s'en assurer en lançant `python3 -m pip freeze` depuis le virtualenv et en vérifiant que rien ne s'affiche. +On peut s'en assurer en lançant ``python3 -m pip freeze`` depuis le virtualenv +et en vérifiant que rien ne s'affiche: -``` -$ python3 -m pip freeze -# de nombreuses bibliothèques en dehors du virtualenv -apipkg==1.5 -cli-ui==0.9.1 -gaupol==1.5 -tabulate==0.8.4 +.. code-block:: console -$ /home/dmerej/foo-venv/bin/python3 -m pip freeze -# rien :) -``` + $ python3 -m pip freeze + # de nombreuses bibliothèques en dehors du virtualenv + apipkg==1.5 + cli-ui==0.9.1 + gaupol==1.5 + tabulate==0.8.4 -On peut alors utiliser le module `pip` *du virtualenv* pour installer des bibliothèques dans celui-ci : +.. code-block:: console -``` -$ /home/dmerej/foo-venv/bin/python3 -m pip install cli-ui -Collecting cli-ui - Using cached https://pythonhosted.org/..cli_ui-0.9.1-py3-none-any.whl -Collecting colorama (from cli-ui) - Using cached https://pythonhosted.org/..colorama-0.4.1-py2.py3-none-any.whl -Collecting unidecode (from cli-ui) - Using cached https://pythonhosted.org/..Unidecode-1.0.23-py2.py3-none-any.whl -Collecting tabulate (from cli-ui) -Installing collected packages: colorama, unidecode, tabulate, cli-ui -Successfully installed cli-ui-0.9.1 colorama-0.4.1 tabulate-0.8.3 - unidecode-1.0.23 -``` + $ /home/dmerej/foo-venv/bin/python3 -m pip freeze + # rien :) -Cette fois, aucune bibliothèque n'est marquée comme déjà installée, et on récupère donc `cli-ui` et toutes ses dépendances. +On peut alors utiliser le module ``pip`` *du virtualenv* pour installer des +bibliothèques dans celui-ci : + +.. code-block:: console + + $ /home/dmerej/foo-venv/bin/python3 -m pip install cli-ui + Collecting cli-ui + Using cached https://pythonhosted.org/..cli_ui-0.9.1-py3-none-any.whl + Collecting colorama (from cli-ui) + Using cached https://pythonhosted.org/..colorama-0.4.1-py2.py3-none-any.whl + Collecting unidecode (from cli-ui) + Using cached https://pythonhosted.org/..Unidecode-1.0.23-py2.py3-none-any.whl + Collecting tabulate (from cli-ui) + Installing collected packages: colorama, unidecode, tabulate, cli-ui + Successfully installed cli-ui-0.9.1 colorama-0.4.1 tabulate-0.8.3 + unidecode-1.0.23 + +Cette fois, aucune bibliothèque n'est marquée comme déjà installée, +et on récupère donc ``cli-ui`` et toutes ses dépendances. On a enfin notre solution pour résoudre notre conflit de dépendances : on peut simplement créer un virtualenv par projet. Ceci nous permettra -d'avoir effectivement deux versions différentes de `cli-ui`, isolées les +d'avoir effectivement deux versions différentes de ``cli-ui``, isolées les unes des autres. -## Activer un virtualenv +Activer un virtualenv +---------------------- -Devoir préciser le chemin du virtualenv en entier pour chaque commande peut devenir fastidieux ; heureusement, il est possible *d'activer* un virtualenv, en lançant une des commandes suivantes : +Devoir préciser le chemin du virtualenv en entier pour chaque commande peut +devenir fastidieux ; heureusement, il est possible *d'activer* un virtualenv, +en lançant une des commandes suivantes : -* `source foo-venv/bin/activate` - si vous utilisez un shell POSIX -* `source foo-venv/bin/activate.fish` - si vous utilisez Fish -* `foo-venv\bin\activate.bat` - sous Windows +* ``source foo-venv/bin/activate`` - si vous utilisez un shell POSIX +* ``source foo-venv/bin/activate.fish`` - si vous utilisez Fish +* ``foo-venv\bin\activate.bat`` - sous Windows -Une fois le virtualenv activé, taper `python`, `python3` ou `pip` utilisera les binaires correspondants dans le virtualenv automatiquement, +Une fois le virtualenv activé, taper ``python``, ``python3`` ou ``pip`` utilisera +les binaires correspondants dans le virtualenv automatiquement, et ce, tant que la session du shell sera ouverte. -Le script d'activation ne fait en réalité pas grand-chose à part modifier la variable `PATH` et rajouter le nom du virtualenv au début de l'invite de commandes : +Le script d'activation ne fait en réalité pas grand-chose à part modifier +la variable ``PATH`` et rajouter le nom du virtualenv au début de l'invite +de commandes: + +.. code-block:: console -``` -# Avant -user@host:~/src $ source foo-env/bin/activate -# Après -(foo-env) user@host:~/src $ -``` + # Avant + user@host:~/src $ source foo-env/bin/activate + # Après + (foo-env) user@host:~/src $ -Pour sortir du virtualenv, entrez la commande `deactivate`. +Pour sortir du virtualenv, entrez la commande ``deactivate``. -## Conclusion +Conclusion +---------- -Le système de gestions des dépendances de Python peut paraître compliqué et bizarre, surtout venant d'autres langages. +Le système de gestions des dépendances de Python peut paraître compliqué +et bizarre, surtout venant d'autres langages. -Mon conseil est de toujours suivre ces deux règles : +Mon conseil est de toujours suivre ces deux règles: * Un virtualenv par projet et par version de Python -* Toujours utiliser `pip` *depuis* un virtualenv +* Toujours utiliser ``pip`` *depuis* un virtualenv -Certes, cela peut paraître fastidieux, mais c'est une méthode qui vous évitera probablement de vous arracher les cheveux (croyez-en mon expérience). +Certes, cela peut paraître fastidieux, mais c'est une méthode qui vous +évitera probablement de vous arracher les cheveux (croyez-en mon expérience). diff --git a/cours/source/14-bibliothèques-01/index.rst b/cours/source/14-bibliothèques-01/index.rst index 64ee138..2032c0e 100644 --- a/cours/source/14-bibliothèques-01/index.rst +++ b/cours/source/14-bibliothèques-01/index.rst @@ -1,4 +1,12 @@ -+++ -title = "Chapitre 14 - Bibliothèques" -weight = 14 -+++ +Chapitre 14 - Bibliothèques +=========================== + +.. toctree:: + :maxdepth: 1 + + 01-rappels + 02-sys.path + 03-bibliotheques-tierces + 04-dépendances + 05-virtualenv + diff --git a/cours/source/conf.py b/cours/source/conf.py index 6d1a07c..26d93a7 100644 --- a/cours/source/conf.py +++ b/cours/source/conf.py @@ -12,6 +12,7 @@ language = "fr" templates_path = ["_templates"] exclude_patterns = [] +html_show_sourcelink = False html_theme_path = [sphinx_nameko_theme.get_html_theme_path()] html_theme = "nameko" html_static_path = ["_static"] diff --git a/cours/source/index.rst b/cours/source/index.rst index 0e1111f..2a6896a 100644 --- a/cours/source/index.rst +++ b/cours/source/index.rst @@ -2,20 +2,20 @@ Programmation en Python ======================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 :caption: Table des matières: - 01-introduction/index - 02-premiers-pas/index - 03-variables-et-types/index - 04-code-source/index - 05-flot-de-controle/index - 06-fonctions/index - 07-listes/index - 08-none-et-pass/index - 09-dictionnaires/index - 10-tuples/index - 11-classes-01/index - 12-modules-01/index - 13-classes-02/index - 14-bibliothèques-01/index + 01-introduction/index + 02-premiers-pas/index + 03-variables-et-types/index + 04-code-source/index + 05-flot-de-controle/index + 06-fonctions/index + 07-listes/index + 08-none-et-pass/index + 09-dictionnaires/index + 10-tuples/index + 11-classes-01/index + 12-modules-01/index + 13-classes-02/index + 14-bibliothèques-01/index