From 7aa73358356b1abe971a0a05c66e8f823215b562 Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Wed, 24 May 2023 10:40:24 +0200 Subject: [PATCH] Python initiation: Moins de maths, plus de bouffe. --- python-initiation/initiation.md | 542 +++++++++++++++++--------------- 1 file changed, 280 insertions(+), 262 deletions(-) diff --git a/python-initiation/initiation.md b/python-initiation/initiation.md index 4b7c85f..201b0b8 100644 --- a/python-initiation/initiation.md +++ b/python-initiation/initiation.md @@ -1,16 +1,26 @@ # Python -::: notes +notes: + +Juste pour le doctest : -Juste pour le doctest: ```python -x, y = 0, 0 +import random +random.seed(42) + +ingredients = ["sel", "farine", "sucre", "levure", "beurre", "œuf"] def randint(a, b): return 4 # https://xkcd.com/221/ -first_primes = [2, 3, 5, 7] -``` +il_reste_de_la_pâte = 0 + +recettes = [ + {'titre': 'Broyés du Poitou', 'ingredients': "farine", "durée": 35}, + {'titre': 'Œufs mimosa', 'ingredients': "", "durée": 20}, + {'titre': 'Houmous', 'ingredients': "", "durée": 13}, +] +``` ## Python @@ -21,11 +31,11 @@ Julien Palard https://mdk.fr -::: notes +notes: Introduce yourself! -Ça couvre les types de bases survol quelques stricutres de contrôle, +Ça couvre les types de bases survol quelques strucutres de contrôle, et quelques fonctions natives. @@ -94,12 +104,12 @@ Parfois appelé le *REPL* ou *la console interactive*. ```bash $ python3 ->>> 10800 / 60 / 60 -3.0 +>>> 5 + 2 +7 >>> ``` -::: notes +notes: Permet d'essayer un peu de Python sans pour autant ouvrir un fichier. @@ -130,7 +140,7 @@ In [1]: 10 ``` -::: notes +notes: L'interpréteur à lu les caractères `1` `0`, a compris que c'était un nombre entier, l'a stocké dans sa représentation interne, un objet, @@ -155,7 +165,7 @@ Traceback (most recent call last): ZeroDivisionError: division by zero ``` -::: notes +notes: Lisez *TOUJOURS* la dernière ligne en premier ! @@ -200,11 +210,11 @@ False ## Chaînes de caractères ```python ->>> "Anne Elk's Theory" -"Anne Elk's Theory" +>>> "Beurre ou huile d'olive ?" +"Beurre ou huile d'olive ?" ``` -::: notes +notes: Expliquer ce qu'est une chaîne, sans parler de pointeurs, on est pas dans un cours de C89. @@ -213,11 +223,11 @@ dans un cours de C89. ## Chaînes de caractères ```python ->>> 'Colin "Bomber" Harris' -'Colin "Bomber" Harris' +>>> 'Cuisson "au beurre" !!' +'Cuisson "au beurre" !!' ``` -::: notes +notes: Les triples quotes apparaissent jour 2. @@ -225,11 +235,11 @@ Les triples quotes apparaissent jour 2. ## Listes ```python ->>> [2, 3, 5, 7] -[2, 3, 5, 7] +>>> ["1 kg de farine", "12 œufs", "130 g de beurre"] +['1 kg de farine', '12 œufs', '130 g de beurre'] ``` -::: notes +notes: La représentation est souvent du Python valide. @@ -237,37 +247,25 @@ La représentation est souvent du Python valide. ## Listes ```python ->>> [1, 1.5, 2, 2.5] -[1, 1.5, 2, 2.5] +>>> ["farine", 1000] +['farine', 1000] ``` -::: notes +notes: Attention à ne pas abuser du mélange autorisé des types. -## Listes - -```python ->>> [[1, 1], [1, 2], [2, 1], [2, 2]] -[[1, 1], [1, 2], [2, 1], [2, 2]] -``` - -::: notes - -Une liste c'est de la donnée, ce qu'elle contint c'est de la donnée. - - ## *n*-uplets, *tuple* ```python ->>> 1, 2 -(1, 2) ->>> "Graham", "John", "Terry" -('Graham', 'John', 'Terry') +>>> ("œufs", 12) +('œufs', 12) +>>> ("sucre", 130) +('sucre', 130) ``` -::: notes +notes: C'est la virgule qui fait le n-uplet, pas les parenthèses. @@ -275,13 +273,25 @@ Pensez au *n*-uplet comme une structure C, *a record*, pas comme une liste, par exemple des coordonnées : (x, y). +## Listes et tuples + +```python +>>> [("farine", 1000), ("œufs", 12), ("sucre", 130)] +[('farine', 1000), ('œufs', 12), ('sucre', 130)] +``` + +notes: + +Une liste c'est de la donnée, ce qu'elle contint c'est de la donnée. + + ## Ensembles ```python -{101, 103, 107, 109} +{"farine", "sucre", "œuf", "levure", "sel", "beurre"} ``` -::: notes +notes: Un ensemble n'est pas ordonné. @@ -289,11 +299,14 @@ Un ensemble n'est pas ordonné. ## Dictionnaires ```python -{"Aval": "Qui se trouve du côté de la vallée.", - "Amont": "Qui se trouve du côté de la montagne."} +{ + "un verre": "25 cl", + "un petit verre": "12 cl", + "qs": "quantité suffisante" +} ``` -::: notes +notes: On associe une valeur à une clé. Utile *seulement* si on ne connaît pas les clefs à l'avance, sinon c'est une classe. @@ -329,11 +342,11 @@ pas les clefs à l'avance, sinon c'est une classe. ```python ->>> "La vie " + "de Brian" -'La vie de Brian' +>>> "Bri" + "oche" +'Brioche' ``` -::: notes +notes: It's called concatenation of strings. @@ -341,11 +354,11 @@ It's called concatenation of strings. ## Les opérateurs ```python ->>> "Tu tum pak " * 2 -'Tu tum pak Tu tum pak ' +>>> "mur" * 2 +'murmur' ``` -::: notes +notes: Tant qu'il n'y a pas d'ambiguité, c'est implémenté. @@ -353,8 +366,8 @@ Tant qu'il n'y a pas d'ambiguité, c'est implémenté. ## Les opérateurs ```python ->>> [2, 3, 5] + [7, 11, 13, 17] -[2, 3, 5, 7, 11, 13, 17] +>>> ["farine", "œufs"] + ["sucre", "sel"] +['farine', 'œufs', 'sucre', 'sel'] ``` @@ -370,7 +383,7 @@ True False ``` -::: notes +notes: Déconseiller l'utilisation de `is`, de toute facons PyLint leur dira quand l'utiliser. @@ -387,7 +400,7 @@ False False ``` -::: notes +notes: On utilisera ça plus tard, avec les structures de contrôle. @@ -395,7 +408,7 @@ On utilisera ça plus tard, avec les structures de contrôle. ## Test d'appartenance ```python ->>> "aa" in "sacré graal" +>>> "chocolat" in "pain au chocolat" True ``` @@ -403,7 +416,7 @@ True ## Test d'appartenance ```python ->>> 7 in {2, 3, 5, 7, 11} +>>> "sel" in {"farine", "œuf", "sucre", "sel", "levure"} True ``` @@ -411,11 +424,11 @@ True ## Travailler avec les ensembles ```python ->>> {1, 2} | {1, 3, 4} == {1, 2, 3, 4} +>>> {"farine", "sel"} | {"œuf", "sel"} == {"sel", "farine", "œuf"} True ``` -::: notes +notes: C'est une union. @@ -423,11 +436,11 @@ C'est une union. ## Travailler avec les ensembles ```python ->>> {"a", "b"} & {"a", "x", "y"} -{'a'} +>>> {"farine", "levure", "sel"} & {"œuf", "sel", "sucre"} +{'sel'} ``` -::: notes +notes: Une intersection. @@ -435,7 +448,7 @@ Une intersection. ## Mais en cas d'ambiguité… ```python ->>> "D'oh!" * "D'oh!" +>>> "farine" * "sucre" Traceback (most recent call last): File "", line 1, in TypeError: can't multiply sequence by non-int of type 'str' @@ -445,7 +458,7 @@ TypeError: can't multiply sequence by non-int of type 'str' ## Mais en cas d'ambiguité… ```python ->>> {"a", "b"} + {"a", "x", "y"} +>>> {"farine", "levure"} + {"sel", "œuf"} Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for +: 'set' and 'set' @@ -457,42 +470,41 @@ TypeError: unsupported operand type(s) for +: 'set' and 'set' ## Affectation ```python ->>> x = 10 ->>> y = 10 ->>> x + y -20 +>>> preparation = 20 +>>> cuisson = 30 +>>> preparation + cuisson +50 ``` -::: notes +notes: -« x » est assigné à 10. - -JAMAIS dire: 10 est assigné à « x ». -JAMAIS JAMAIS dire : On met 10 dans « x ». +JAMAIS dire : On met 20 dans « preparation ». ## Affectation multiple ```python ->>> x, y = 2, 3 ->>> x -2 ->>> y -3 +>>> preparation, cuisson = 20, 30 +>>> preparation +20 +>>> cuisson +30 ``` ## Accès par indice ```python ->>> first_primes = [2, 3, 5, 7] ->>> first_primes[0] -2 ->>> first_primes[1] -3 +>>> etapes = ["preparation", "cuisson", "dégustation"] +>>> etapes[0] +'preparation' +>>> etapes[1] +'cuisson' +>>> etapes[2] +'dégustation' ``` -::: notes +notes: On réutilise le nom pour accéder au contenu. @@ -502,9 +514,14 @@ Bien prendre le temps d'expliquer la syntaxe ici. ## Accès par clé ```python ->>> d = {"zero": 0, "un": 1, "deux": 2} ->>> d["deux"] + d["deux"] -4 +>>> definitions = { +... "un verre": "25 cl", +... "un petit verre": "12 cl", +... "qs": "quantité suffisante" +... } +... +>>> definitions["un petit verre"] +'12 cl' ``` @@ -513,11 +530,11 @@ Bien prendre le temps d'expliquer la syntaxe ici. ## print ```python ->>> print("zero") -zero +>>> print("Brioche") +Brioche ``` -::: notes +notes: C'est leur première fonction, s'attarder sur la syntaxe ! @@ -525,11 +542,11 @@ C'est leur première fonction, s'attarder sur la syntaxe ! ## print ```python ->>> print("√2 =", 2 ** 0.5) -√2 = 1.4142135623730951 +>>> print("La moitié de 130 g :", 130 // 2, "g") +La moitié de 130 g : 65 g ``` -::: notes +notes: En effet, le P de REPL étant `print`, le print est implicite dans un REPL. @@ -544,28 +561,28 @@ Exercices: ## str, list, int, ... ```python ->>> str(12) -'12' +>>> str(130) +'130' ``` ## str, list, int, ... ```python ->>> int("12") -12 +>>> int("130") +130 ``` ## len ```python ->>> len([1, 2, 3]) +>>> len(["farine", "œufs", "sucre"]) 3 ->>> len("Bonjour") -7 +>>> len("farine") +6 ``` -::: notes +notes: Exercise: Character counting @@ -588,7 +605,7 @@ Affiche la documentation de n'importe quoi, essayez : - `help(list)` - ... -::: notes +notes: Accepte aussi une valeur (et donc une variable) mais attention : si la variable est une chaîne, `help` n'affichera pas la documentation des @@ -598,19 +615,19 @@ chaînes. ## sorted ```python ->>> sorted({2, 1, 7, 6}) -[1, 2, 6, 7] +>>> sorted({"sel", "farine", "sucre"}) +['farine', 'sel', 'sucre'] ``` ## Les importer des modules ```python -from random import choice - -print(choice(["Pizzeria", "Japonais"])) +>>> from random import choice +>>> print(choice(["Pain au chocolat", "Chocolatine"])) +Pain au chocolat ``` -::: notes +notes: Exercice : Import. @@ -620,11 +637,12 @@ Exercice : Import. ## if ```python -if 1 in {2, 3, 5, 7, 11}: - print("1 serait-il premier ?") +>>> if "sucre" in ingredients and "œuf" in ingredients: +... print("Commencer par blanchir les œufs.") +Commencer par blanchir les œufs. ``` -::: notes +notes: Parler de l'indentation ! @@ -639,10 +657,11 @@ whole number can be written as a _unique_ product of primes ». Après un bloc `if`, on peut ajouter un bloc `else` : ```python -if x % 2 == 0: - print("x est pair.") -else: - print("x est impair.") +>>> if "sucre" in ingredients and "œuf" in ingredients: +... print("Commencer par blanchir les œufs.") +... else: +... print("Commencer comme vous voulez.") +Commencer par blanchir les œufs. ``` @@ -651,17 +670,19 @@ else: Après un `if`, on peut ajouter un ou des bloc `elif` : ```python -if (x, y) == (0, 0): - print("Point à l'origine") -elif x == 0: - print("Point sur l'abscisse") -elif y == 0: - print("Point sur l'ordonnée") -else: - print("Point ailleurs") +>>> grams = 1250 +>>> if grams == 0: +... print("Ne pas en mettre") +... elif grams < 1000: +... print("En mettre", grams, "grammes.") +... else: +... kg, g = divmod(grams, 1000) +... print("En mettre", kg, "kg", "et", g, "g.") +... +En mettre 1 kg et 250 g. ``` -::: notes +notes: Parler de `pass` et de `...`. @@ -669,40 +690,39 @@ Parler de `pass` et de `...`. ## for ```python ->>> for un_nombre in first_primes: -... print(un_nombre) +>>> for ingredient in ingredients: +... print(ingredient) ... -2 -3 -5 -7 +sel +farine +sucre +levure +beurre +œuf ``` -Équivalent à : - ```python -un_nombre = first_primes[0] -print(un_nombre) -un_nombre = first_primes[1] -print(un_nombre) -un_nombre = first_primes[2] -print(un_nombre) -un_nombre = first_primes[3] -print(un_nombre) +>>> ingredient = ingredients[0] +>>> print(ingredient) +sel +>>> ingredient = ingredients[1] +>>> print(ingredient) +farine +>>> ingredient = ingredients[2] +>>> print(ingredient) +sucre ``` ## for ```python ->>> for letter in "Hello": -... print(letter) +>>> for lettre in "Œuf": +... print(lettre) ... -H -e -l -l -o +Œ +u +f >>> ``` @@ -719,10 +739,11 @@ o 4 ``` -::: notes +notes: Exercice : Square numbers, powers of two, comparisons. + ## L'instruction `while` Très rarement utilisée car le `for` est bien plus pratique, sert @@ -735,84 +756,83 @@ cependant dans quelques cas: ## L'instruction `while` ```python ->>> sq = 5 ->>> guess = 2 ->>> error = abs(sq - guess * guess) ->>> while error > 0.0001: -... guess = (guess + sq / guess) / 2 -... error = abs(sq - guess * guess) ->>> guess -2.2360679779158037 +>>> while il_reste_de_la_pâte: +... faire_une_crêpe() +... ``` -::: notes - -C'est la méthode de Héron, mais ne pas rentrer dans le détail, juste -montrer la ligne du while, les laisser revenir dessus plus tard s'ils -le veulent. - # Les méthodes ## Sur les chaînes + ```python ->>> s = "The prime numbers." ->>> s.title() -'The Prime Numbers.' ->>> s.startswith("The") +>>> s = "Pâte à crêpe" +>>> s.count("e") +2 +>>> s.startswith("Pâte") True >>> s.split() -['The', 'prime', 'numbers.'] +['Pâte', 'à', 'crêpe'] ``` -::: notes +notes: Exercise : Counting Words. + ## Sur les chaînes ```python ->>> s = "phi = {}" ->>> s.format((1 + 5 ** 0.5) / 2) -'phi = 1.618033988749895' +>>> s = "Durée : {} minutes." +>>> s.format(3600 // 60) +'Durée : 60 minutes.' ``` ## Sur les listes ```python ->>> l = [2, 3, 5, 7] ->>> l.append(11) ->>> l.sort(reverse=True) ->>> l -[11, 7, 5, 3, 2] +>>> ingredients = ["1 kg de farine", "12 œufs"] +>>> ingredients.append("130 g de beurre") +>>> ingredients +['1 kg de farine', '12 œufs', '130 g de beurre'] ``` ## Sur les dictionnaires ```python ->>> d = {"Aval": "Du côté de la vallée.", -... "Amont": "Du côté de la montagne."} ->>> d.items() -dict_items([('Aval', 'Du côté de la vallée.'), ('Amont', 'Du côté de la montagne.')]) +>>> definitions = { +... "un verre": "25 cl", +... "un petit verre": "12 cl", +... "qs": "quantité suffisante" +... } +>>> definitions.items() +dict_items([('un verre', '25 cl'), ('un petit verre', '12 cl'), ('qs', 'quantité suffisante')]) ``` # Les variables (suite) - ## Le type des variables En Python, les variables ne sont que des noms. -*Des « étiquettes » qu'on colle aux objets.* +Des « étiquettes » qu'on colle aux objets. -Seul les valeurs sont typées. +> Comme sur les pots de confiture. -*Toutes les valeurs sont des objets.* -::: notes +## Le type des variables + +Seules les valeurs sont typées. + +> L'étiquette n'a pas de goût, c'est la confiture qui a du goût. + +notes: + +Toutes les valeurs sont des objets. Sans. Exceptions. @@ -825,7 +845,7 @@ C'est pour ça que pour `n = 10` on dit "n est assigné à 10", et non "10 est m Certains types sont modifiables, d'autres, non. -::: notes +notes: On dit qu'elles sont immuables (*immutable* en anglais). @@ -840,7 +860,7 @@ de constantes. - On peut supprimer une clef d'un dictionnaire. - ... -::: notes +notes: - Listes - Dictionnaires @@ -855,7 +875,7 @@ de constantes. - Ni qu'une paire contient maintenant trois éléments. - ... -::: notes +notes: - Les chaînes - Les *n*-uplets @@ -883,7 +903,7 @@ False False ``` -::: notes +notes: Attention à la sémantique : `if foo` est différent de `if foo is True`. @@ -897,23 +917,25 @@ dire quand même : pour `True`, `False`, et `None`. ```python def ma_fonction(ses_paramètres): - ... # Le corps de la fonction + ... # Le code de la fonction ``` -::: notes +notes: Passer du temps sur la syntaxe et le vocabulaire - fonction - paramètre, argument - `return` + ## Exemple ```python -def add(a, b): - return a + b - -print(add(1, 2)) +>>> def temps_total(temps_de_preparation, temps_de_cuisson): +... return temps_de_preparation + temps_de_cuisson +... +>>> temps_total(30, 20) +50 ``` ## Paramètres @@ -921,39 +943,38 @@ print(add(1, 2)) Une fonction prend des paramètres et renvoie une valeur. ```python -def is_even(value): - return value % 2 == 0 +def fahrenheit_to_celsius(fahrenheit): + return (fahrenheit - 32) * 5/9 ``` ## Arguments On peut donc lui donner des arguments : ```python ->>> print(is_even(10)) -True ->>> print(is_even(11)) -False ->>> print(is_even(12)) -True ->>> print(is_even(13)) -False +>>> fahrenheit_to_celsius(320) +160.0 +>>> fahrenheit_to_celsius(390) +198.88888888888889 +>>> fahrenheit_to_celsius(450) +232.22222222222223 ``` -::: notes +notes: Exercices: First function, Print even numbers, ... # Les chaînes -::: notes +```python +>>> """Elle préfère "l'huile d'olive".""" +'Elle préfère "l\'huile d\'olive".' +``` + +notes: ~ fin de jour 1 début de jour 2 -```python ->>> """Anne Elk's Theory est "mieux".""" -'Anne Elk\'s Theory est "mieux".' -``` ## Les docstrings @@ -981,7 +1002,7 @@ def une_fonction(): 0.30000000000000004 ``` -::: notes +notes: https://0.30000000000000004.com @@ -990,35 +1011,33 @@ https://0.30000000000000004.com ## `break` et `continue` -Break sert à interrompre une boucle, continue sert à passer à l'élément -suivant. Qu'on soit dans un `for` ou dans un `while`. +`break` sert à interrompre une boucle, `continue` sert à passer à +l'élément suivant. Qu'on soit dans un `for` ou dans un `while`. ## `break` ```python ->>> sq, gues = 5, 2 >>> while True: -... gues = (gues + sq / gues) / 2 -... error = abs(sq - gues * gues) -... if error < 0.0001: +... if not il_reste_de_la_pâte: ... break ->>> gues -2.2360679779158037 +... faire_une_crêpe() +... ``` ## `continue` ```python-repl ->>> for i in range(5): -... if i == 0: +>>> for recette in recettes: +... if "sésame" in recette["ingredients"]: ... continue -... print(i) -1 -2 -3 -4 +... if "noix de Pécan" in recette["ingredients"]: +... continue +... print(recette["titre"]) +Broyés du Poitou +Œufs mimosa +Houmous ``` @@ -1026,7 +1045,7 @@ suivant. Qu'on soit dans un `for` ou dans un `while`. ```python >>> try: -... int("abc") +... int("2 kg") ... except ValueError: ... print("Raté") Raté @@ -1038,22 +1057,21 @@ Raté C'est transformer ça : ```python ->>> accumulator = [] ->>> for i in range(10): -... accumulator.append(2 ** i) ->>> accumulator -[1, 2, 4, 8, 16, 32, 64, 128, 256, 512] +>>> durées = [] +>>> for recette in recettes: +... durées.append(recette["durée"]) +>>> durées +[35, 20, 13] ``` - ## La notation en compréhension en : ```python ->>> [2 ** i for i in range(10)] -[1, 2, 4, 8, 16, 32, 64, 128, 256, 512] +>>> [recette["durée"] for recette in recettes] +[35, 20, 13] ``` @@ -1062,12 +1080,12 @@ en : Ou : ```python -def phi(n): - numbers = [] - for i in range(n): - if math.gcd(i, n) == 1: - numbers.append(i) - return len(numbers) +def compte_recettes(recettes, ingrédient): + trouvées = [] + for recette in recettes: + if ingrédient in recette["ingredients"]: + trouvées.append(recette) + return len(trouvées) ``` @@ -1076,11 +1094,17 @@ def phi(n): en : ```python -def phi(n): - return len([i for i in range(n) if math.gcd(i, n) == i]) +def compte_recettes(recettes, ingrédient): + return len( + [ + recette + for recette in recettes + if ingrédient in recette["ingredients"] + ] + ) ``` -::: notes +notes: Elle devrait s'écrire sur une seule ligne, mais, vidéoprojecteur... @@ -1090,9 +1114,12 @@ Elle devrait s'écrire sur une seule ligne, mais, vidéoprojecteur... *slices* en anglais +On pourrait traduire par « tranches » et filer la métaphore culinaire… + Notes: ```python -seq = list("ABCDEF") +seq = 'incontestable' +# ['incontestable', 'contestable', 'testable', 'stable'] ``` @@ -1100,17 +1127,17 @@ seq = list("ABCDEF") ```python >>> seq -['A', 'B', 'C', 'D', 'E', 'F'] +'incontestable' >>> seq[0] -'A' +'i' ``` ## Les *slices* ```python ->>> seq[0:3] -['A', 'B', 'C'] +>>> seq[5:13] +'testable' ``` @@ -1393,15 +1420,6 @@ Notes: Deux ans après la fonction fait 800 lignes, et personne ne l'a vu venir. flake8 peut aider. -## Garder son API évolutive - -Utilisez correctement `/` et `*` dans les prototypes de fonction. - -Notes: - -help(sum) - - ## Les « linters » Il existe plusieurs outils pour « relire » votre code : @@ -1552,8 +1570,8 @@ Signifie « plusieurs, nommés », comme dans un dictionnaire. ```python >>> def p(**kwargs): -... for key, value in kwargs.items(): -... print(key, "→", value) +... for key, value in kwargs.items(): +... print(key, "→", value) ... >>> p(x=10, y=12) x → 10