Python avancé: Couper le trivia pour libérer du temps.
This commit is contained in:
parent
313092b0d9
commit
cecadcf4bc
|
@ -1,4 +1,13 @@
|
||||||
# Python
|
# Python avancé
|
||||||
|
|
||||||
|
### Présenté par
|
||||||
|
|
||||||
|
<!-- .slide: data-background="static/background.jpg" -->
|
||||||
|
|
||||||
|
Julien Palard <julien@palard.fr>
|
||||||
|
|
||||||
|
https://mdk.fr
|
||||||
|
|
||||||
|
|
||||||
Notes:
|
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.
|
- Détaillez toutes les étapes, même les plus petites.
|
||||||
- Soyez drôle ! Donnez envie !
|
- Soyez drôle ! Donnez envie !
|
||||||
|
|
||||||
|
|
||||||
J'ai 5 jours, donc ~200 slides.
|
J'ai 5 jours, donc ~200 slides.
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,12 +101,10 @@ Déjà, c'est pas un objet.
|
||||||
Jusqu'où peut-on creuser ?
|
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érable,
|
||||||
- Itérateur : Représentation d'un flux d'éléments.
|
- itérateur.
|
||||||
- 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.
|
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
|
@ -108,8 +116,6 @@ mais aussi une séquence, une collections, ...
|
||||||
|
|
||||||
Implémente `__getitem__` et `__len__`.
|
Implémente `__getitem__` et `__len__`.
|
||||||
|
|
||||||
(voir meme `__reversed__`, `__iter__` et `__contains__`).
|
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
Exercice, implémenter un `range()`, mais sans `stop` ni `step`.
|
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 ?
|
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
|
## Le protocole d'itération
|
||||||
|
|
||||||
- `iter()` : Crée un itérateur à partir d'un itérable.
|
- `iter()` : Crée un itérateur à partir d'un itérable.
|
||||||
- `next()` : Demande l'élément suivant à un itérateur.
|
- `next()` : Demande l'élément suivant à un itérateur.
|
||||||
|
- `raise StopIteration` : C'est terminé.
|
||||||
|
|
||||||
Notes:
|
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).
|
vérifications (que l'itérateur renvoyé soit bien un itérateur).
|
||||||
|
|
||||||
|
|
||||||
## Petite parenthèse
|
## Duck typing
|
||||||
|
|
||||||
```python
|
```python
|
||||||
>>> class Counter:
|
>>> class Counter:
|
||||||
|
@ -185,168 +172,77 @@ Via le protocole séquence, `__len__` n'est pas utilisé donc ça se
|
||||||
passe bien.
|
passe bien.
|
||||||
|
|
||||||
|
|
||||||
## Petite parenthèse
|
## Ne pas confondre
|
||||||
|
|
||||||
```python
|
Itérateur :
|
||||||
>>> class B: ...
|
|
||||||
...
|
|
||||||
>>> iter(B())
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
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 "<stdin>", line 1, in <module>
|
|
||||||
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
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class CounterIterator:
|
class CounterIterator:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.i = -1
|
self.i = -1
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
def __next__(self):
|
def __next__(self):
|
||||||
self.i += 1
|
self.i += 1
|
||||||
return self.i
|
return self.i
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Solution
|
## Ne pas confondre
|
||||||
|
|
||||||
```python
|
```pycon
|
||||||
>>> c = BetterCounter()
|
>>> c = CounterIterator()
|
||||||
>>> for i, j in zip(c, c):
|
>>> for i in c:
|
||||||
... if i > 5: break
|
... print(i)
|
||||||
... print(i, j)
|
... if i >= 2: break
|
||||||
0 0
|
...
|
||||||
1 1
|
0
|
||||||
2 2
|
1
|
||||||
3 3
|
2
|
||||||
4 4
|
>>> for i in c:
|
||||||
5 5
|
... print(i)
|
||||||
|
... if i >= 2: break
|
||||||
|
...
|
||||||
|
3
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
C'est toujours faux ! Un itérateur doit AUSSI implémenter `__iter__`,
|
## Ne pas confondre
|
||||||
donc qui `return self`, ça permet d'utiliser aussi les itérateurs avec
|
|
||||||
for.
|
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 ?
|
## 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
|
```python
|
||||||
class GenCounter:
|
class GenCounter:
|
||||||
def __iter__(self):
|
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`
|
## Pendant qu'on parle de `for`
|
||||||
|
|
||||||
Connaissez-vous le `else` du `for` ?
|
Connaissez-vous le `else` du `for` ?
|
||||||
|
@ -392,38 +283,6 @@ Typiquement utile lors des recherches, la sémantique :
|
||||||
|
|
||||||
Fonctionne aussi sur le while.
|
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
|
## 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`, ...
|
- Parler de `head, *rest`, ...
|
||||||
|
|
||||||
|
|
||||||
## Ça peut rappeler `*args` et `**kwargs`
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Démo si nécessaire.
|
|
||||||
|
|
||||||
|
|
||||||
# Les objets
|
# Les objets
|
||||||
|
|
||||||
## Rappels
|
## Rappels
|
||||||
|
@ -452,7 +304,7 @@ Démo si nécessaire.
|
||||||
- Flat is better than nested.
|
- Flat is better than nested.
|
||||||
|
|
||||||
|
|
||||||
## `classmethod` vs `staticmethod`
|
## `classmethod`, `staticmethod`
|
||||||
|
|
||||||
## La MRO
|
## La MRO
|
||||||
|
|
||||||
|
@ -474,8 +326,10 @@ Antisèche : https://wyz.fr/3Z8
|
||||||
|
|
||||||
## Le protocole « descripteur »
|
## Le protocole « descripteur »
|
||||||
|
|
||||||
- `object.__get__(self, instance, owner=None)`
|
```python
|
||||||
- `object.__set__(self, instance, value)`
|
def __get__(self, instance, owner=None): ...
|
||||||
|
def __set__(self, instance, value): ...
|
||||||
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
|
@ -530,38 +384,6 @@ Vous pouvez aussi utiliser un décorateur pour personaliser une classe.
|
||||||
|
|
||||||
# Langage
|
# 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
|
## IEEE 754
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -591,13 +413,6 @@ Notes:
|
||||||
library/exceptions.html → hierarchy
|
library/exceptions.html → hierarchy
|
||||||
|
|
||||||
|
|
||||||
## try, finally, else, except
|
|
||||||
|
|
||||||
Dans quel ordre ?
|
|
||||||
|
|
||||||
Notes: Oui, il y a un else ici aussi.
|
|
||||||
|
|
||||||
|
|
||||||
## try, except, else, finally
|
## try, except, else, finally
|
||||||
|
|
||||||
## Les gestionnaires de contexte
|
## Les gestionnaires de contexte
|
||||||
|
@ -615,9 +430,10 @@ En avancé on apprend à en faire.
|
||||||
|
|
||||||
## Les gestionnaires de contexte
|
## Les gestionnaires de contexte
|
||||||
|
|
||||||
- ``__enter__``
|
```python
|
||||||
- ``__exit__``
|
def __enter__(self): ...
|
||||||
|
def __exit__(self, exc_type, exc_value, tb): ...
|
||||||
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
|
@ -757,19 +573,6 @@ Notes:
|
||||||
- `@contextmanager`
|
- `@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
|
## The Walrus Operator
|
||||||
|
|
||||||
`:=`
|
`:=`
|
||||||
|
|
1
python-avancé/static/background.jpg
Symbolic link
1
python-avancé/static/background.jpg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../background.jpg
|
Loading…
Reference in New Issue
Block a user