diff --git a/python-avancé/python-avancé.md b/python-avancé/python-avancé.md index 49de929..d312bbb 100644 --- a/python-avancé/python-avancé.md +++ b/python-avancé/python-avancé.md @@ -1,4 +1,13 @@ -# Python +# Python avancé + +### Présenté par + + + +Julien Palard + +https://mdk.fr + Notes: @@ -13,6 +22,7 @@ en avancé on crée (des itérables par exemple). - Détaillez toutes les étapes, même les plus petites. - Soyez drôle ! Donnez envie ! + J'ai 5 jours, donc ~200 slides. @@ -91,12 +101,10 @@ Déjà, c'est pas un objet. Jusqu'où peut-on creuser ? -## `for` itère sur des itérables +## `for` itère des itérables -- Itérable : Objet dont on peut obtenir les éléments un à un. -- Itérateur : Représentation d'un flux d'éléments. -- Séquence : Un itérable dont les éléments sont accessible par indice et dont on connaît la taille. -- Collection : Itérable dont on connaît la longueur. +- Itérable, +- itérateur. Notes: @@ -108,8 +116,6 @@ mais aussi une séquence, une collections, ... Implémente `__getitem__` et `__len__`. -(voir meme `__reversed__`, `__iter__` et `__contains__`). - Notes: Exercice, implémenter un `range()`, mais sans `stop` ni `step`. @@ -117,30 +123,11 @@ Exercice, implémenter un `range()`, mais sans `stop` ni `step`. Petite parenthèse : `range`, c'est une classe ou une fonction ? -## Le protocole « séquence » - -`__getitem__` suffit pour être itérable. - -Notes: - -C'est l'application du duck-typing : Si ça a tout ce dont `for` à -besoin, alors ça fonctionne. `for` n'a pas besoin de connaître la -taille, donc ça fonctionne. - - -## Le protocole d'itération - -Notes: - -Itérable : Objet capable de renvoyer ses éléments un à un. -Itérateur : Objet chargé de s'occuper de l'itération d'un itérable : -se souvenir où on en est. - - ## Le protocole d'itération - `iter()` : Crée un itérateur à partir d'un itérable. - `next()` : Demande l'élément suivant à un itérateur. +- `raise StopIteration` : C'est terminé. Notes: @@ -161,7 +148,7 @@ protocole séquence soit le protocole d'itération, et fait quelques vérifications (que l'itérateur renvoyé soit bien un itérateur). -## Petite parenthèse +## Duck typing ```python >>> class Counter: @@ -185,168 +172,77 @@ Via le protocole séquence, `__len__` n'est pas utilisé donc ça se passe bien. -## Petite parenthèse +## Ne pas confondre -```python ->>> class B: ... -... ->>> iter(B()) -Traceback (most recent call last): - File "", line 1, in -TypeError: 'B' object is not iterable -``` - -Notes: - -`iter()` donne une belle exception. - - -## Petite parenthèse - -```python ->>> class C: -... def __iter__(self): return None -... ->>> iter(C()) -Traceback (most recent call last): - File "", line 1, in -TypeError: iter() returned non-iterator of type 'NoneType' -``` - -Notes: - -`iter()` valide que `__iter__` renvoie bien un itérateur. - - -## Digression - -`iter` a aussi une version qui prend deux paramètres. - -```python -from functools import partial - -with open('mydata.db', 'rb') as f: - for block in iter(partial(f.read, 64), b''): - process_block(block) -``` - - -## Retour sur `__iter__` - -```python - def __iter__(self): - return self -``` - -Notes: - -Mauvaise idée ! - -Réimplémentez la classe `Counter()` comme ça. - - -## Solution - -```python -class Counter: - def __init__(self): self.i = -1 - def __iter__(self): return self - def __next__(self): - self.i += 1 - return self.i -``` - -## Le problème - -```python ->>> c = Counter() ->>> for i, j in zip(c, c): -... print(i, j) -... if i > 5: break -... -0 1 -2 3 -4 5 -6 7 -``` - - -## On recommence - -Notes: - -Cette fois avec un itérateur dédié. - - -## Solution - -```python -class BetterCounter: - def __iter__(self): - return CounterIterator() -``` - - -## Solution +Itérateur : ```python class CounterIterator: def __init__(self): self.i = -1 + def __iter__(self): + return self + def __next__(self): self.i += 1 return self.i ``` -## Solution +## Ne pas confondre -```python ->>> c = BetterCounter() ->>> for i, j in zip(c, c): -... if i > 5: break -... print(i, j) -0 0 -1 1 -2 2 -3 3 -4 4 -5 5 +```pycon +>>> c = CounterIterator() +>>> for i in c: +... print(i) +... if i >= 2: break +... +0 +1 +2 +>>> for i in c: +... print(i) +... if i >= 2: break +... +3 ``` -Notes: -C'est toujours faux ! Un itérateur doit AUSSI implémenter `__iter__`, -donc qui `return self`, ça permet d'utiliser aussi les itérateurs avec -for. +## Ne pas confondre + +Et itérable : + +```python +class CounterIterable: + def __iter__(self): + return CounterIterator() +``` + + +## Ne pas confondre + +```python +>>> c = CounterIterable() +>>> for i in c: +... print(i) +... if i >= 2: break +... +0 +1 +2 +>>> for i in c: +... print(i) +... if i >= 2: break +... +0 +1 +2 +``` ## Peut-on faire plus simple ? -Notes: - -Oui ! Avec un générateur ! C'est le sucre syntaxique pour créer ses -itérables. - -Attention, une fonction générateur renvoie un itérateur, (qu'on -appelle un générateur), pas un itérable ! Et là on est bien contents -qu'un itérateur ai un `__iter__` qui se renvoie lui même, pour pouvoir -l'utiliser dans un for ! - - -## Mais alors - -Si une fonction générateur renvoie un itérateur, et que `__iter__` -doit renvoyer un itérateur, on peut implémenter `__iter__` avec yield ? - -Notes: - -Oui. - - -## Exemple - ```python class GenCounter: def __iter__(self): @@ -357,11 +253,6 @@ class GenCounter: ``` -## Pendant qu'on parle de `yield` - -Connaissez-vous `yield from` ? - - ## Pendant qu'on parle de `for` Connaissez-vous le `else` du `for` ? @@ -392,38 +283,6 @@ Typiquement utile lors des recherches, la sémantique : Fonctionne aussi sur le while. -Ah j'ai utilisé une f-string. - - -## Literal String Interpolation - -```python ->>> f"{42:08b}" -'00101010' -``` - -Notes: - -Attention aux ':' et '!' dans l'expression, bien que ce soit accepté -si c'est entre guillemet, crochets, parenthèses, ... sinon toute -expression Python est autorisée (comme avec .format, mais avec .format -c'est plus évident). - - -## Literal String Interpolation - -```python ->>> f"{(lambda x: x.upper())('hello'):^11}" -' HELLO ' -``` - -Notes: - -Attention à rester lisible, mais ici le `:` de la lambda est entre -parenthèses, donc c'est bon. - -En parlant de parenthèse, fermons une parenthèse. - ## On parlais d'itérables @@ -437,13 +296,6 @@ Pour se remémorer ces choses, cherchez les PEPs, typiquement la 448, la 3132, . - Parler de `head, *rest`, ... -## Ça peut rappeler `*args` et `**kwargs` - -Notes: - -Démo si nécessaire. - - # Les objets ## Rappels @@ -452,7 +304,7 @@ Démo si nécessaire. - Flat is better than nested. -## `classmethod` vs `staticmethod` +## `classmethod`, `staticmethod` ## La MRO @@ -474,8 +326,10 @@ Antisèche : https://wyz.fr/3Z8 ## Le protocole « descripteur » -- `object.__get__(self, instance, owner=None)` -- `object.__set__(self, instance, value)` +```python +def __get__(self, instance, owner=None): ... +def __set__(self, instance, value): ... +``` Notes: @@ -530,38 +384,6 @@ Vous pouvez aussi utiliser un décorateur pour personaliser une classe. # Langage -## `id` et `is` - -Notes: - -- `is` : pour les singletons `None`, `True`, `False`. -- `id` : identifiant unique, l'adresse mémoire en CPython. -- `is` : proche de `id(left) == id(right)` mais attention au GC. - - -## Parenthèse sur les singletons - -Notes: - -Un module est un singleton. - - -## String interning - -```python -a = "Bonjour !" -b = "Bonjour !" -a is b -``` - -? - -Notes: - -- C'est dépendant de l'implémentation, ça change d'une version à l'autre de Python. -- Les chaînes ne contenant que des [a-zA-Z0-9_] sont internées. - - ## IEEE 754 ```python @@ -591,13 +413,6 @@ Notes: library/exceptions.html → hierarchy -## try, finally, else, except - -Dans quel ordre ? - -Notes: Oui, il y a un else ici aussi. - - ## try, except, else, finally ## Les gestionnaires de contexte @@ -615,9 +430,10 @@ En avancé on apprend à en faire. ## Les gestionnaires de contexte -- ``__enter__`` -- ``__exit__`` - +```python +def __enter__(self): ... +def __exit__(self, exc_type, exc_value, tb): ... +``` Notes: @@ -757,19 +573,6 @@ Notes: - `@contextmanager` -## contextlib - -Un décorateur peut-il être aussi un gestionnaire de contexte ? - -Est-ce utile ? Pertinent ? - -Notes: - -Oui, par exemple Django `@atomic` et with `atomic:`, `contextlib.ContextDecorator`. - -Parler des gestionnaires de contextes réutilisables, puis réentrants. - - ## The Walrus Operator `:=` diff --git a/python-avancé/static/background.jpg b/python-avancé/static/background.jpg new file mode 120000 index 0000000..fbaf874 --- /dev/null +++ b/python-avancé/static/background.jpg @@ -0,0 +1 @@ +../../background.jpg \ No newline at end of file