## Python Par Julien Palard
https://mdk.fr notes: Introduce yourself! Ça couvre les types de bases survol quelques strucutres de contrôle, et quelques fonctions natives. Et juste pour le doctest : ```python import random random.seed(42) ingredients = ["sel", "farine", "sucre", "levure", "beurre", "œuf"] def randint(a, b): return 4 # https://xkcd.com/221/ 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 : Introduction Python est un langage de programmation permettant de s'exprimer de manière **concise** et **lisible**.
## Qui utilise Python ? YouTube, Dropbox, Reddit, Instagram, Spotify, NASA…
## Combien ?
## Quelle version ?
## Installation https://python.org — ou — 
## Démarrer un interpréteur Sur Windows : ```text py ``` Sur tous les autres OS : ```text python3 ```
## L'interpréteur Parfois appelé le *REPL* ou *la console interactive*. ```bash $ python3 >>> 5 + 2 7 >>> ``` notes: Permet d'essayer un peu de Python sans pour autant ouvrir un fichier. Et oui, même après 10 ans de Python, on l'utilise encore. Expliquer les parties "R", "E", "P", "L".
## L'interpréteur Il en existe plusieurs : Celui natif à Python, IDLE, IPython, … Il ressemble généralement soit à ça : ```bash >>> ``` soit à ça : ```bash In [1]: ```
## Testons l'interpréteur ```pycon >>> 10 10 ``` 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, puis nous l'a représenté à son tour avec deux caractères `1` et `0` pour qu'on puisse le lire.
## C'est votre nouvelle calculatrice ```pycon >>> 60 * 60 * 4 14400 ```
## Les exceptions ```pycon >>> 5 / 0 Traceback (most recent call last): File "
", line 1, in
ZeroDivisionError: division by zero ``` notes: Lisez *TOUJOURS* la dernière ligne en premier !
## Types natifs
## Booléens ```pycon >>> True True >>> False False ```
## Nombres ```pycon >>> 42 42 ```
## Nombres ```pycon >>> 18446744073709551616 18446744073709551616 ```
## Nombres ```pycon >>> 3.1415 3.1415 ```
## Chaînes de caractères ```pycon >>> "Beurre ou huile d'olive ?" "Beurre ou huile d'olive ?" ``` notes: Expliquer ce qu'est une chaîne, sans parler de pointeurs, on est pas dans un cours de C89.
## Chaînes de caractères ```pycon >>> 'Cuisson "au beurre" !!' 'Cuisson "au beurre" !!' ``` notes: Les triples quotes apparaissent jour 2.
## Listes ```pycon >>> ["1 kg de farine", "12 œufs", "130 g de beurre"] ['1 kg de farine', '12 œufs', '130 g de beurre'] ``` notes: La représentation est souvent du Python valide.
## Listes ```pycon >>> ["farine", 1000] ['farine', 1000] ``` notes: Attention à ne pas abuser du mélange autorisé des types.
## *n*-uplets, *tuple* ```pycon >>> ("œufs", 12) ('œufs', 12) >>> ("sucre", 130) ('sucre', 130) ``` notes: C'est la virgule qui fait le n-uplet, pas les parenthèses. Pensez au *n*-uplet comme une structure C, *a record*, pas comme une liste, par exemple des coordonnées : (x, y).
## Listes et tuples ```pycon >>> [("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 {"farine", "sucre", "œuf", "levure", "sel", "beurre"} ``` notes: Un ensemble n'est pas ordonné.
## Dictionnaires ```python { "un verre": "25 cl", "un petit verre": "12 cl", "qs": "quantité suffisante" } ``` notes: On associe une valeur à une clé. Utile *seulement* si on ne connaît pas les clefs à l'avance, sinon c'est une classe.
## Les opérateurs
## Les opérateurs mathématiques ```pycon >>> 10 + 10 20 >>> 10.5 + 2 12.5 ```
## Les opérateurs mathématiques ```pycon >>> (4 * 10**1) + (2 * 10**0) 42 ```
## Les opérateurs mathématiques ```pycon >>> 10 / 2 5.0 ```
## Les opérateurs ```pycon >>> "Bri" + "oche" 'Brioche' ``` notes: It's called concatenation of strings.
## Les opérateurs ```pycon >>> "mur" * 2 'murmur' ``` notes: Tant qu'il n'y a pas d'ambiguité, c'est implémenté.
## Les opérateurs ```pycon >>> ["farine", "œufs"] + ["sucre", "sel"] ['farine', 'œufs', 'sucre', 'sel'] ```
## Les Comparisons ```pycon >>> 10 < 1 False >>> 10 == 10 True >>> 10 >= 20 False ``` notes: Déconseiller l'utilisation de `is`, de toute facons PyLint leur dira quand l'utiliser.
## Logique ```pycon >>> True or False True >>> True and False False >>> not True False ``` notes: On utilisera ça plus tard, avec les structures de contrôle.
## Test d'appartenance ```pycon >>> "chocolat" in "pain au chocolat" True ```
## Test d'appartenance ```pycon >>> "sel" in {"farine", "œuf", "sucre", "sel", "levure"} True ```
## Travailler avec les ensembles ```pycon >>> {"farine"} | {"œuf", "sucre"} == {"sucre","farine","œuf"} True ``` notes: C'est une union.
## Travailler avec les ensembles ```pycon >>> {"farine", "levure", "sel"} & {"œuf", "sel", "sucre"} {'sel'} ``` notes: Une intersection.
## Mais en cas d'ambiguité… ```pycon >>> "farine" * "sucre" Traceback (most recent call last): File "
", line 1, in
TypeError: can't multiply sequence by non-int of type 'str' ```
## Les variables
## Affectation ```pycon >>> preparation = 20 >>> cuisson = 30 >>> preparation + cuisson 50 ``` notes: JAMAIS dire : On met 20 dans « preparation ».
## Affectation multiple ```pycon >>> preparation, cuisson = 20, 30 >>> preparation 20 >>> cuisson 30 ```
## Accès par indice ```pycon >>> etapes = ["preparation", "cuisson", "dégustation"] >>> etapes[0] 'preparation' >>> etapes[1] 'cuisson' >>> etapes[2] 'dégustation' ``` notes: On réutilise le nom pour accéder au contenu. Bien prendre le temps d'expliquer la syntaxe ici.
## Accès par clé ```pycon >>> definitions = { ... "un verre": "25 cl", ... "un petit verre": "12 cl", ... "qs": "quantité suffisante" ... } ... >>> definitions["un petit verre"] '12 cl' ```
## Les fonctions
## print ```pycon >>> print("Brioche") Brioche ``` notes: C'est leur première fonction, s'attarder sur la syntaxe !
## print ```pycon >>> print("La moitié de 130 g :", 130 // 2, "g") La moitié de 130 g : 65 g ``` notes: En effet, le P de REPL étant `print`, le print est implicite dans un REPL. Mais le REPL sert a tester : on peut bien tester print dans le REPL. Exercices: - Print 42 - Number of seconds in a year - Using operators
## str, list, int, ... ```pycon >>> str(130) '130' ```
## str, list, int, ... ```pycon >>> int("130") 130 ```
## len ```pycon >>> len(["farine", "œufs", "sucre"]) 3 >>> len("farine") 6 ``` notes: Exercise: Character counting
## range ```pycon >>> list(range(10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(range(5, 10)) [5, 6, 7, 8, 9] ```
## help Affiche la documentation de n'importe quoi, essayez : - `help(str)` - `help(list)` - ... 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 chaînes.
## sorted ```pycon >>> sorted({"sel", "farine", "sucre"}) ['farine', 'sel', 'sucre'] ```
## importer des modules ```pycon >>> from random import choice >>> print(choice(["Pain au chocolat", "Chocolatine"])) Pain au chocolat ``` notes: Exercice : Import.
## Les instructions
## if ```pycon >>> if "sucre" in ingredients and "œuf" in ingredients: ... print("Commencer par blanchir les œufs.") Commencer par blanchir les œufs. ``` notes: Parler de l'indentation ! Notez le `...`, on a du appyer un coup en « entrée » pour fermer ce bloc. 1 était premier, avant, mais ça casse le théorème « Every possible whole number can be written as a _unique_ product of primes ».
## Le `else` Après un bloc `if`, on peut ajouter un bloc `else` : ```pycon >>> 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. ```
## Le `elif` Après un `if`, on peut ajouter un ou des bloc `elif` : ```pycon >>> 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: Parler de `pass` et de `...`.
## for ```pycon >>> for ingredient in ingredients: ... print(ingredient) ... sel farine sucre levure beurre œuf ``` ```pycon >>> ingredient = ingredients[0] >>> print(ingredient) sel >>> ingredient = ingredients[1] >>> print(ingredient) farine ``` …
## for ```pycon >>> for lettre in "Œuf": ... print(lettre) ... Œ u f >>> ```
## for ```pycon >>> for i in range(5): ... print(i) 0 1 2 3 4 ``` notes: Exercice : Square numbers, powers of two, comparisons.
## L'instruction `while` Très rarement utilisée car le `for` est bien plus pratique, sert cependant dans quelques cas: - `while True:` - `while il_reste_du_travail_à_faire:`
## L'instruction `while` ```pycon >>> while il_reste_de_la_pâte: ... faire_une_crêpe() ... ```
## Les méthodes
## Sur les chaînes ```pycon >>> s = "Pâte à crêpe" >>> s.count("e") 2 >>> s.startswith("Pâte") True >>> s.split() ['Pâte', 'à', 'crêpe'] ``` notes: Exercise : Counting Words.
## Sur les chaînes ```pycon >>> s = "Durée : {} minutes." >>> s.format(3600 // 60) 'Durée : 60 minutes.' ```
## Sur les listes ```pycon >>> 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 ```pycon >>> 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. > Comme sur les pots de confiture.
## 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. On peut « coller » plusieurs étiquettes à une même valeur. C'est pour ça que pour `n = 10` on dit "n est assigné à 10", et non "10 est mis dans n".
## Immuables vs modifiables Certains types sont modifiables, d'autres, non. notes: On dit qu'elles sont immuables (*immutable* en anglais). Attention, les variables sont toujours ... variables, nous n'avons pas de constantes.
## Les types modifiables - On peut ajouter à une liste. - On peut vider un ensemble. - On peut supprimer une clef d'un dictionnaire. - ... notes: - Listes - Dictionnaires - Ensembles - ...
## Les types immuables - On ne peut pas dire que maintenant 10 vaut 12. - Ni que faux devient vrai. - Ni qu'une paire contient maintenant trois éléments. - ... notes: - Les chaînes - Les *n*-uplets - Les entiers - Les booléens - ... Pour les chaînes c'est discutable, mais avoir des chaînes immuables est confortable (clef de dictionnaires par exemple, ou la garantie qu'un appel à une fonction avec une chaîne en paramètre ne va pas la modifier).
## La vérité En Python, ce qui est vide est faux, `0` est faux, `None` est faux, `False` est faux. Le reste est vrai : ```pycon >>> bool("Non vide") True >>> bool([]) # Une liste vide False >>> bool(0.0) False ``` notes: Attention à la sémantique : `if foo` est différent de `if foo is True`. Leur rappeler que c'est pylint qui leur dira quand utiliser `is`, leur dire quand même : pour `True`, `False`, et `None`.
## Les fonctions
## Syntaxe ```python def ma_fonction(ses_paramètres): ... # Le code de la fonction ``` notes: Passer du temps sur la syntaxe et le vocabulaire - fonction - paramètre, argument - `return`
## Exemple ```pycon >>> def temps_total(temps_de_preparation, temps_de_cuisson): ... return temps_de_preparation + temps_de_cuisson ... >>> temps_total(30, 20) 50 ```
## Paramètres Une fonction prend des paramètres et renvoie une valeur. ```python def fahrenheit_to_celsius(fahrenheit): return (fahrenheit - 32) * 5/9 ```
## Arguments On peut donc lui donner des arguments : ```pycon >>> fahrenheit_to_celsius(320) 160.0 >>> fahrenheit_to_celsius(390) 198.88888888888889 >>> fahrenheit_to_celsius(450) 232.22222222222223 ``` notes: Exercices: First function, Print even numbers, ...
## Les chaînes ```pycon >>> """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
## Les docstrings ```python def une_fonction(): """Une courte description.""" ... return ... ```
## Les nombres
## Les opérateurs mathématiques ```pycon >>> 0.1 + 0.1 0.2 ```
## Les opérateurs mathématiques ```pycon >>> 0.1 + 0.2 0.30000000000000004 ``` notes: https://0.30000000000000004.com
## `for` et `while`
## `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` ```pycon >>> while True: ... if not il_reste_de_la_pâte: ... break ... faire_une_crêpe() ... ```
## `continue` ```pycon-repl >>> for recette in recettes: ... if "sésame" in recette["ingredients"]: ... continue ... if "noix de Pécan" in recette["ingredients"]: ... continue ... print(recette["titre"]) Broyés du Poitou Œufs mimosa Houmous ```
## Les exceptions : `try` ```pycon >>> try: ... int("2 kg") ... except ValueError: ... print("Raté") Raté ```
## La notation en compréhension C'est transformer ça : ```pycon >>> durées = [] >>> for recette in recettes: ... durées.append(recette["durée"]) >>> durées [35, 20, 13] ```
## La notation en compréhension en : ```pycon >>> [recette["durée"] for recette in recettes] [35, 20, 13] ```
## La notation en compréhension Ou : ```python 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) ```
## La notation en compréhension en : ```python def compte_recettes(recettes, ingrédient): return len( [ recette for recette in recettes if ingrédient in recette["ingredients"] ] ) ``` notes: Elle devrait s'écrire sur une seule ligne, mais, vidéoprojecteur...
## Les *slices* *slices* en anglais notes: On pourrait traduire par « tranches » et filer la métaphore culinaire… ['incontestable', 'contestable', 'testable', 'stable'] ```python seq = 'incontestable' ```
## Les *slices* ```pycon >>> seq = 'incontestable' >>> seq[0] 'i' ```
## Les *slices* ```text 11 0123456789 | incontestable—12 | 10 ``` ```pycon >>> seq[5:9] 'test' >>> seq[5:13] 'testable' >>> seq[7:13] 'stable' ```
## Les *slices* ```pycon >>> seq[:2] 'in' >>> seq[2:] 'contestable' ```
## Les *slices* ```pycon >>> seq[-1] 'e' ```
## Les *slices* ```pycon >>> seq[-6:] 'stable' ```
## Les *slices* ```pycon >>> "perso"[::2] 'pro' ``` notes: ```python dict = ['ados', 'assit', 'engager', 'éroder', 'épater', 'ivres', 'soda', 'tissa', 'regagne', 'erodé', 'retapé', 'servi'] ```
## Les *slices* ```pycon >>> for word in dict: ... if word[::-1] in dict: ... print(word, word[::-1]) ados soda assit tissa engager regagne épater retapé ivres servi soda ados tissa assit regagne engager retapé épater servi ivres ```
## Les *slices* `seq[
:
:
]`
## Les classes
## La syntaxe ```python class LeNomDeLaClasse: """Sa docstring, comme pour une fonction. """ # Le corps de la classe. ``` Notes: Dédiaboliser l'héritage, l'héritage multiple, les interfaces, les classes abstraites, les méthodes virtuelles, ...
## À retenir On pourrait trier les données en deux types : - celles dont on connaît les attributs → classes - celles dont on ne connaît pas les attributs → dictionnaires Notes: Le contexte, le métier, les bibliothèques utilisées peuvent générer des cas particuliers, cette règle n'est pas absolue. préciser peut être : "dont on connaît à toutes les étapes du programme".
## Exemple ```python class Recette: def __init__(self, nom, ingrédients): self.nom = nom self.ingrédients = ingrédients ``` Notes: On connaît les attributs.
## Exemple ```pycon >>> ingrédients = { ... 'farine': 1000, ... 'eau': 615, ... 'levure': 10, ... 'sel': 20 ... } ``` Notes: On ne les connaît pas.
## Syntaxe → les méthodes ```python class Dice: def throw(self): self.value = randint(1, 6) ``` Notes: Une classe *ne sert pas* à stocker des fonctions, mais des données. Pensez aux structs C. La données d'abord, l'algorithme après.
## Syntaxe → le constructeur ```python class DiceCup: def __init__(self, dices): self.dices = dices def shake(self): ... ``` Notes: Leur faire faire implémenter le __repr__, min, max, et mean. Leur faire faire 1000 tirages dans un Counter, avec des valeurs ENTIẼRES. BiaisedDice(Dice) est un bon exemple d'héritage.
## Utilisation ```pycon >>> dice = Dice() >>> dice.throw() >>> dice.value 4 ```
## Utilisation ```pycon >>> cup = DiceCup( ... [Dice() for _ in range(10)] ... ) >>> cup.shake() ``` Notes: Faire quelques exemples d'héritage simples avant de passer a la suite. super() considered super() !
## pip, venvs, conda
## pip C'est l'outil standard pour installer un paquet. ```bash $ python3 -m pip install
``` Mais, ça installe où ?
## venv C'est l'outil standard pour indiquer où installer les paquets. *On peut ainsi avoir plusieurs environnements, un par projet par exemple*. Notes: Pratique pour avoir des versions différentes.
## venv ```bash $ python3 -m venv --prompt test .venv/ $ source .venv/bin/activate (test) $ python3 -m pip install pytest ``` Notes: Dépendant du shell, les envoyer sur library/venv.html. Insister sur le côté "trashable du venv" : - Ne rien mettre dans .venv - rm -fr .venv # au moindre souci
## conda ```bash $ conda create --name test $ conda activate test (test) $ conda install numpy ```
## Tester
## pytest ```bash (test) $ mkdir tests/ (test) $ pip install pytest (test) $ editor tests/test_dice.py ``` Notes: C'est l'occasion de parler de assert.
## hypothesis ```python from hypothesis import given from hypothesis.strategies import floats @given(floats()) def test_temperature_conversions(temp): assert fahrenheit_to_celsius( celsius_to_fahrenheit(temp)) == temp ```
## Les bonnes pratiques Notes: Prérequis: pip et venv. ~fin jour 2 / début jour 3.
## Bonnes habitudes > There are 2 hard problems in computer science: cache invalidation, > naming things, and off-by-1 errors.
## Bonnes habitudes Pas plus de 7.
## Les « linters » Il existe plusieurs outils pour « relire » votre code : - flake8, - pylint, - mypy, - black, - bandit, - isort. Et un pour les unifier tous : `tox`. Notes: Leur faire implémenter un `is_prime(x)` pour jouer avec. La règle des 7.
## flake8 Dans un venv : ```bash (test) $ pip install flake8 (test) $ pip install flake8-bugbear (test) $ flake8 --max-complexity 9 *.py ``` Notes: Flake8 est rapide, n'effectuant pas les `imports` il ne peut repérer qu'une catégorie de problèmes. 9 est trop bas, 15 est probablement un bon choix.
## pylint ```bash (test) $ pip install pylint (test) $ pylint is_prime.py ```
## mypy `mypy` fonctionne avec des annotations de types. ```bash (test) $ pip install mypy (test) $ mypy is_prime.py ``` Notes: --ignore-missing-imports
## bandit Bandit cherche les failles de sécurité... ```bash (test) $ pip install bandit (test) $ bandit is_prime.py ```
## tox Permet de lancer les tests: - sur plusieurs interpréteurs (s'ils sont installés), - de plusieurs outils, - en parallèle. Notes: c.f. gh/JulienPalard/oeis.
## pdb ``` breakpoint() ```
## PYTHONDEVMODE=y Notes: Voir mon bashrc :] Surtout "viable" depuis la 3.8.
## `*`, `**`
## `*` Signifie « plusieurs », comme dans une liste ou un *n*-uplet. ```pycon >>> begin, *rest = range(5) >>> begin 0 >>> rest [1, 2, 3, 4] ``` Notes: Attention, initiation : Le but est de savoir que ça existe, savoire le lire. Équivaut à: begin, rest = seq[0], seq[1:]
## `*` ```pycon >>> def sum(*args): ... print(args) ... >>> sum(1, 2, 3, 4, 5) (1, 2, 3, 4, 5) ```
## `*` ```pycon >>> [0, 0, 0, *range(3)] [0, 0, 0, 0, 1, 2] ```
## `*` ```pycon >>> print(*range(5)) 0 1 2 3 4 ```
## `**` Signifie « plusieurs, nommés », comme dans un dictionnaire. ```pycon >>> def p(**kwargs): ... for key, value in kwargs.items(): ... print(key, "→", value) ... >>> p(x=10, y=12) x → 10 y → 12 ```
## `**` ```pycon >>> defaults = {"path": "./", ... "pattern": "*.txt"} >>> {**defaults, "pattern": "*.md"} {'path': './', 'pattern': '*.md'} ```
## `**` ```pycon >>> def p(x, y): ... print(f"x → {x}") ... print(f"y → {y}") ... >>> point = {"x": 10, "y": 12} >>> p(**point) x → 10 y → 12 ```
## L'encodage
## encoder C'est transformer une chaîne en octets, pour son transport ou stockage : ```pycon >>> "Pâte à crêpe".encode() b'P\xc3\xa2te \xc3\xa0 cr\xc3\xaape' >>> list("Pâte à crêpe".encode()) [80, 195, 162, 116, 101, 32, 195, 160, 32, 99, 114, 195, 170, 112, 101] ```
## décoder C'est le contraire ... ```pycon >>> b'P\xc3\xa2te \xc3\xa0 cr\xc3\xaape'.decode() 'Pâte à crêpe' ``` Notes: Parler d'unicode, d'UTF-8, de latin-1. Ne pas oubilier de mentionner que latin-1 et companie sont à taille fixe, et qu'UTF-8 est à taille variable.
## Les modules utiles - argparse - re - csv (quand on a pas Pandas) - subprocess
## La communauté > Come for the language, stay for the community — Brett Cannon
## Les PyCons La PyConFR (tous les ans) ! Les meetups locaux ! - https://pycon.fr - https://discuss.afpy.org/c/meetups/12