Python avancé: Merge files.
This commit is contained in:
parent
7aa7335835
commit
596e9c5ae1
|
@ -1,444 +0,0 @@
|
||||||
# Python
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
En initiation, on utilise (le for par exemple),
|
|
||||||
en avancé on crée (des itérables par exemple).
|
|
||||||
|
|
||||||
- Exemples concrets et définitions abstraites
|
|
||||||
- Pas de `class Foo`, le cerveau ne peut s'accrocher à rien.
|
|
||||||
- Le bonheur est dans le chemin et dans la finalité
|
|
||||||
- Contenu différenciant
|
|
||||||
- Pas de `class Foo`, tout le monde le fait déjà.
|
|
||||||
- Détaillez toutes les étapes, même les plus petites.
|
|
||||||
- Soyez drôle ! Donnez envie !
|
|
||||||
|
|
||||||
J'ai 5 jours, donc ~200 slides.
|
|
||||||
|
|
||||||
|
|
||||||
## « Tout est objet »
|
|
||||||
|
|
||||||
Comme en Java, #oupas
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
- Sortir un interpréteur.
|
|
||||||
- Leur faire essayer de deviner ce qui pourrait ne pas être une classe.
|
|
||||||
- Démo avec:
|
|
||||||
- un nombre entier, #obvious, c'est géré par Python
|
|
||||||
- ouvrir une parenthèse si nécessaire, avec 6 ** 6 ** 6
|
|
||||||
- un float, en les faisant hésiter vu qu'ils sont « gérés par le CPU »
|
|
||||||
- une fonction
|
|
||||||
- une classe (et une instance)
|
|
||||||
- range !
|
|
||||||
- module !!
|
|
||||||
|
|
||||||
OK mais pas `for`, `def`, ... ce sont des mots clefs.
|
|
||||||
|
|
||||||
|
|
||||||
## Donc, tout a des attributs…
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Exercice : avec des `set`, et `dir()`, trouver la liste des attributs
|
|
||||||
communs à une fonction, disons `max` et à un int, disons `42`,
|
|
||||||
combien y'en-a-il ? Moi 23. Combien `object` en a-il ?
|
|
||||||
|
|
||||||
|
|
||||||
## Même un int ?
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> (42).__bool__() is bool(42)
|
|
||||||
True
|
|
||||||
```
|
|
||||||
|
|
||||||
Ou `help(42 .to_bytes)`.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Ouvrir une parenthèse sur la notion de vérité, ce qui est :
|
|
||||||
|
|
||||||
- Vide
|
|
||||||
- Égal à zéro
|
|
||||||
- None ou False
|
|
||||||
|
|
||||||
c'est faux, le reste, c'est vrai.
|
|
||||||
|
|
||||||
|
|
||||||
## Les noms
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Faire le schéma à deux colonnes: noms → mémoire.
|
|
||||||
https://dreampuf.github.io/GraphvizOnline/#%23%20import%20math%0A%0A%23%20def%20print_tau()%3A%0A%23%20%20%20...%0A%0A%0Adigraph%20G%20%7B%0A%0A%20%20subgraph%20cluster_0%20%7B%0A%20%20%20%20%20label%20%3D%20%22Noms%22%3B%0A%20%20%20%20%20math%3B%0A%20%20%20%20%20print_tau%3B%0A%20%20%7D%0A%0A%20%20subgraph%20cluster_1%20%7B%0A%20%20%20%20%20label%20%3D%20%22Objets%22%3B%0A%20%20%20%20%20%22%3Cmodule%20math%3E%22%0A%20%20%20%20%20%22%3Cfunction%20print_tau%3E%22%0A%20%20%7D%0A%20%20%0A%20%20math%20-%3E%20%22%3Cmodule%20math%3E%22%0A%20%20print_tau%20-%3E%20%22%3Cfunction%20print_tau%3E%22%0A%7D
|
|
||||||
|
|
||||||
En Python avancé bien insister sur le fait qu'un objet en mémoire à
|
|
||||||
une adresse.
|
|
||||||
|
|
||||||
Insister sur le fait qu'un paramètre de fonction n'est qu'un nom. On a
|
|
||||||
donc pas de « passage par valeur » chez nous.
|
|
||||||
|
|
||||||
Bien préciser qu'on ne peut pas « délier » un nom pour le faire
|
|
||||||
pointer sur rien (en ce cas on le fait pointer sur `None`).
|
|
||||||
|
|
||||||
|
|
||||||
# J'ai 5mn pour vous parler de `for`
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Déjà, c'est pas un objet.
|
|
||||||
|
|
||||||
Jusqu'où peut-on creuser ?
|
|
||||||
|
|
||||||
|
|
||||||
## `for` itère sur 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.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
On peut très bien imaginer un itérateur capable d'itérer un itérable,
|
|
||||||
mais aussi une séquence, une collections, ...
|
|
||||||
|
|
||||||
|
|
||||||
## Le protocole « séquence »
|
|
||||||
|
|
||||||
Implémente `__getitem__` et `__len__`.
|
|
||||||
|
|
||||||
(voir meme `__reversed__`, `__iter__` et `__contains__`).
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Première démo REPL sur une liste « on reste utilisateurs de Python ».
|
|
||||||
|
|
||||||
|
|
||||||
## Le protocole d'itération
|
|
||||||
|
|
||||||
`__iter__` et `__next__`
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Démo REPL sur une liste « on perçoit comment on va pouvoir
|
|
||||||
l'implémenter ».
|
|
||||||
|
|
||||||
La différence ? Petite parenthèse : `iter()` peut utiliser soit le
|
|
||||||
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
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> class Counter:
|
|
||||||
... def __getitem__(self, i):
|
|
||||||
... return i
|
|
||||||
...
|
|
||||||
>>> i = iter(Counter())
|
|
||||||
>>> i
|
|
||||||
<iterator object at ...>
|
|
||||||
>>> next(i)
|
|
||||||
0
|
|
||||||
>>> next(i)
|
|
||||||
1
|
|
||||||
>>> next(i)
|
|
||||||
2
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Via le protocole séquence, `__len__` n'est pas utilisé donc ça se
|
|
||||||
passe bien.
|
|
||||||
|
|
||||||
|
|
||||||
## Petite parenthèse
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> 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
|
|
||||||
class CounterIterator:
|
|
||||||
def __init__(self):
|
|
||||||
self.i = -1
|
|
||||||
|
|
||||||
def __next__(self):
|
|
||||||
self.i += 1
|
|
||||||
return self.i
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Solution
|
|
||||||
|
|
||||||
```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
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
## 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):
|
|
||||||
i = 0
|
|
||||||
while True:
|
|
||||||
yield i
|
|
||||||
i += 1
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Pendant qu'on parle de `yield`
|
|
||||||
|
|
||||||
Connaissez-vous `yield from` ?
|
|
||||||
|
|
||||||
|
|
||||||
## Pendant qu'on parle de `for`
|
|
||||||
|
|
||||||
Connaissez-vous le `else` du `for` ?
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Il ne s'exécute que si le `for` sort sans `break`.
|
|
||||||
|
|
||||||
|
|
||||||
## `else`
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> n = 13
|
|
||||||
>>> for i in range(2, n - 1):
|
|
||||||
... if n % i == 0:
|
|
||||||
... print(f"{n} is not prime")
|
|
||||||
... break
|
|
||||||
... else:
|
|
||||||
... print(f"{n} is prime")
|
|
||||||
13 is prime
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Typiquement utile lors des recherches, la sémantique :
|
|
||||||
- Trouvé, plus besoin de chercher, break.
|
|
||||||
- else: pas trouvé.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Si on parlais d'unpacking ?
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Pour se remémorer ces choses, cherchez les PEPs, typiquement la 448, la 3132, ...
|
|
||||||
|
|
||||||
- Parler de `deep unpacking`.
|
|
||||||
- Parler de `head, *rest`, ...
|
|
||||||
|
|
||||||
|
|
||||||
## Ça peut rappeler `*args` et `**kwargs`
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Démo si nécessaire.
|
|
|
@ -1,82 +0,0 @@
|
||||||
# Les objets
|
|
||||||
|
|
||||||
## Rappels
|
|
||||||
|
|
||||||
- Keep it simple.
|
|
||||||
- Flat is better than nested.
|
|
||||||
|
|
||||||
|
|
||||||
## `classmethod` vs `staticmethod`
|
|
||||||
|
|
||||||
## La MRO
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Simple démo REPL : `bool.__mro__`.
|
|
||||||
|
|
||||||
## `super()` !
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Et la coopération, démo avec deux classes : `TCPConnection` qui prend
|
|
||||||
`host, port, timeout`, et `HTTPConnection` qui prend url, method, ...`
|
|
||||||
|
|
||||||
Démo aussi : passer un argument de trop et voir que object() se plains.
|
|
||||||
|
|
||||||
Antisèche : https://wyz.fr/3Z8
|
|
||||||
|
|
||||||
|
|
||||||
## Le protocole « descripteur »
|
|
||||||
|
|
||||||
- `object.__get__(self, instance, owner=None)`
|
|
||||||
- `object.__set__(self, instance, value)`
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Et `__delete__` et `__set_name__`.
|
|
||||||
|
|
||||||
- instance... c'est l'instance.
|
|
||||||
- owner, c'est le type, il est toujours connu donc "devrait" toujours être donné
|
|
||||||
- Si instance n'est pas donnée, c'est qu'on accède à l'attribut sur le type.
|
|
||||||
|
|
||||||
Exercice : https://www.hackinscience.org/exercises/temperature-class
|
|
||||||
|
|
||||||
|
|
||||||
## Métaclasses
|
|
||||||
|
|
||||||
Puisqu'une classe est un objet, une métaclasse c'est le type du type.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
En initiation on dit "ça ne vous servira pas". En avancé on dit
|
|
||||||
`__init_subclass__` couvrira tous vos besoins.
|
|
||||||
|
|
||||||
|
|
||||||
## Métaclasse
|
|
||||||
|
|
||||||
- `__new__` et `__init__` d'une classe servent à personaliser l'instance.
|
|
||||||
- `__new__` et `__init__` d'une metaclasse servent à personalier une classe.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class M(type):
|
|
||||||
def __new__(cls, *args, **kwargs):
|
|
||||||
print(f"meta.__new__(*{args}, **{kwargs})")
|
|
||||||
return super().__new__(cls, *args, **kwargs)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
print(f"meta.__init__(*{args}, **{kwargs})")
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
class MyCls(metaclass=M):
|
|
||||||
def __new__(cls, *args, **kwargs):
|
|
||||||
print(f"cls.__new__(*{args}, **{kwargs})")
|
|
||||||
return super().__new__(cls, *args, **kwargs)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
print(f"cls.__init__(*{args}, **{kwargs})")
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
```
|
|
||||||
|
|
||||||
Vous pouvez aussi utiliser un décorateur pour personaliser une classe.
|
|
|
@ -1,307 +0,0 @@
|
||||||
# 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
|
|
||||||
f"http://{.1 + .2}.com"
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Notez ! Et au besoin utilisez le module Decimal.
|
|
||||||
|
|
||||||
|
|
||||||
## Définir vos propres exceptions
|
|
||||||
|
|
||||||
Il suffit d'hériter d'`Exception`, rien de plus.
|
|
||||||
|
|
||||||
```
|
|
||||||
>>> class DBError(Exception): pass
|
|
||||||
...
|
|
||||||
>>> raise DBError("No such entry")
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
__main__.DBError: No such entry
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
```python
|
|
||||||
with open("/etc/hosts") as f:
|
|
||||||
f.read()
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
En initiation on apprend a les utiliser.
|
|
||||||
En avancé on apprend à en faire.
|
|
||||||
|
|
||||||
|
|
||||||
## Les gestionnaires de contexte
|
|
||||||
|
|
||||||
- ``__enter__``
|
|
||||||
- ``__exit__``
|
|
||||||
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Expliquer le protocole.
|
|
||||||
|
|
||||||
|
|
||||||
## Les gestionnaires de contexte
|
|
||||||
|
|
||||||
```python
|
|
||||||
class transaction:
|
|
||||||
def __init__(self, db):
|
|
||||||
self.db = db
|
|
||||||
def __enter__(self):
|
|
||||||
self.db.begin()
|
|
||||||
def __exit__(self, type, value, tb):
|
|
||||||
if type is None:
|
|
||||||
self.db.commit()
|
|
||||||
else:
|
|
||||||
self.db.rollback()
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
C'est un exemple de gestionnaire de contexte de transaction de base de donnée.
|
|
||||||
|
|
||||||
Astuce, `__enter__` peut renvoyer un tuple, qu'on peut décomposer à
|
|
||||||
droite du as, typiquement `ifile`, `ofile`.
|
|
||||||
|
|
||||||
|
|
||||||
## Les décorateurs
|
|
||||||
|
|
||||||
`@`
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
En initiation on apprend a les utiliser.
|
|
||||||
En avancé on apprend à en faire.
|
|
||||||
|
|
||||||
|
|
||||||
Just for doctest:
|
|
||||||
```python
|
|
||||||
def clock(f=None, *args, **kwargs):
|
|
||||||
return lambda *args: None
|
|
||||||
```
|
|
||||||
|
|
||||||
## Les décorateurs
|
|
||||||
|
|
||||||
```python
|
|
||||||
@clock
|
|
||||||
def fib(n):
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
équivaut à
|
|
||||||
|
|
||||||
```python
|
|
||||||
fib = clock(fib)
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Bien insister sur le fait que `@` est bien séparé de son
|
|
||||||
`dotted_name`, pas n'importe quelle expression. sur le fait qu'on
|
|
||||||
peut les empiler (clarifier l'ordre).
|
|
||||||
|
|
||||||
|
|
||||||
## Les décorateurs
|
|
||||||
|
|
||||||
```python
|
|
||||||
@clock(deadline=10)
|
|
||||||
def fib(n):
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
équivaut à
|
|
||||||
|
|
||||||
```python
|
|
||||||
fib = clock(deadline=10)(fib)
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Rappeler que `()` n'est qu'un opérateur.
|
|
||||||
|
|
||||||
|
|
||||||
## Les décorateurs
|
|
||||||
|
|
||||||
Faire ses propres décorateurs.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Leur faire implémenter un décorateur @clock.
|
|
||||||
```python
|
|
||||||
def clock(func):
|
|
||||||
def clocked(*args):
|
|
||||||
t0 = time.perf_counter()
|
|
||||||
result = func(*args)
|
|
||||||
elapsed = time.perf_counter() -t0
|
|
||||||
name = func.__name__
|
|
||||||
arg_str = ', '.join(repr(arg) for arg in args)
|
|
||||||
print(f"[{elapsed:.08f}s] {name}({arg_str}) -> {result!r}")
|
|
||||||
return result
|
|
||||||
return clocked
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Les décorateurs
|
|
||||||
|
|
||||||
Faire ses décorateurs paramétrés.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Leur faire implémenter @memoize qui prend en paramètre une limite.
|
|
||||||
|
|
||||||
En profiter pour parler de `global`, `nonlocal`, et des closures.
|
|
||||||
|
|
||||||
|
|
||||||
## Les décorateurs
|
|
||||||
|
|
||||||
Les utiliser pour leurs effets de bord.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
`@route("/")` par exemple.
|
|
||||||
|
|
||||||
|
|
||||||
## Les décorateurs
|
|
||||||
|
|
||||||
- `@staticmethod`
|
|
||||||
- `@classmethod`
|
|
||||||
- `@property`
|
|
||||||
|
|
||||||
|
|
||||||
## contextlib
|
|
||||||
|
|
||||||
- `with suppress:`
|
|
||||||
- `@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
|
|
||||||
|
|
||||||
`:=`
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Démo REPL avec re.match, rappeler que les parenthèses sont souvent
|
|
||||||
obligatoires.
|
|
||||||
|
|
||||||
|
|
||||||
## Les listes en compréhension
|
|
||||||
|
|
||||||
```python
|
|
||||||
l = []
|
|
||||||
for i in range(5):
|
|
||||||
if i % 2 == 0:
|
|
||||||
for j in range(5):
|
|
||||||
if j % 2 == 0:
|
|
||||||
for k in range(5):
|
|
||||||
if k % 2 == 0:
|
|
||||||
if i + j + k == 4:
|
|
||||||
l.append((i,j,k))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Les listes en compréhension
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> [(i, j, k)
|
|
||||||
... for i in range(5)
|
|
||||||
... if i % 2 == 0
|
|
||||||
... for j in range(5)
|
|
||||||
... if j % 2 == 0
|
|
||||||
... for k in range(5)
|
|
||||||
... if k % 2 == 0
|
|
||||||
... if i + j + k == 4]
|
|
||||||
[(0, 0, 4), (0, 2, 2), (0, 4, 0), (2, 0, 2), (2, 2, 0), (4, 0, 0)]
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Juste pour doctest:
|
|
||||||
|
|
||||||
```python
|
|
||||||
factors = lambda i: [i]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Les listes en compréhension
|
|
||||||
|
|
||||||
```python
|
|
||||||
{x: factors(x)
|
|
||||||
for x in range(1000)
|
|
||||||
if len(factors(x)) == 3}
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes: si factors est lent (spoiler: il l'est), c'est du gâchis,
|
|
||||||
utiliser un walrus !
|
|
||||||
|
|
||||||
|
|
||||||
## Les listes en compréhension
|
|
||||||
|
|
||||||
```python
|
|
||||||
{x: prime_factors
|
|
||||||
for x in range(1000)
|
|
||||||
if len(prime_factors := factors(x)) == 3}
|
|
||||||
```
|
|
|
@ -1,57 +0,0 @@
|
||||||
# L'encodage
|
|
||||||
|
|
||||||
## Les octets d'abord
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> bytes([0x01, 0x02]) == b"\x01\x02"
|
|
||||||
True
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Notez qu'en hexadecimal, deux symboles permet de représenter
|
|
||||||
exactement 8 bits, donc exactement un octet.
|
|
||||||
|
|
||||||
|
|
||||||
## ASCII
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
1960, 7 bits ("a word", qu'on a traduit "un octet"), [0; 127]
|
|
||||||
|
|
||||||
Seul la moitié des octets sont donc de l'ASCII valide.
|
|
||||||
|
|
||||||
Exercice: Utiliser `range()` et `bytes([i])` pour afficher la table ascii.
|
|
||||||
|
|
||||||
|
|
||||||
## Latin-1
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
1985, 8 bits, [0; 255]
|
|
||||||
|
|
||||||
Couvre environ 32 langues.
|
|
||||||
|
|
||||||
Quasi complet pour le francais, il manque juste le Œ, le œ (le
|
|
||||||
francais qui s'en est occupé n'était pas linguiste.)
|
|
||||||
|
|
||||||
|
|
||||||
Exercice: Utiliser `range()` et `bytes([i])` pour afficher la table latin-1.
|
|
||||||
|
|
||||||
|
|
||||||
## Unicode
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
~1990, d'abord sur 16 bits, aujourd'hui c'est juste une base de donnée.
|
|
||||||
|
|
||||||
Couvre environ 150 langues (environ toutes).
|
|
||||||
|
|
||||||
Calque latin1 de 0 à 255, même C0 (controles bien définis) et C1 (controles
|
|
||||||
ignorés, de 0x80 à 0x9F).
|
|
||||||
|
|
||||||
|
|
||||||
## encoder, décoder
|
|
||||||
|
|
||||||
- `str.encode` → `bytes`
|
|
||||||
- `bytes.decode` → `str`
|
|
|
@ -1,98 +0,0 @@
|
||||||
# Le packaging
|
|
||||||
|
|
||||||
## Petite parenthèse
|
|
||||||
|
|
||||||
La différence entre un paquet et un module ?
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Pour Python il n'y en a pas, tout est module, pour nous, un paquet est
|
|
||||||
un dossier. Aborder rapidement les paquets-espace-de-noms.
|
|
||||||
|
|
||||||
|
|
||||||
## Digression
|
|
||||||
|
|
||||||
`__main__` et `__main__.py`.
|
|
||||||
|
|
||||||
|
|
||||||
## venv
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Et ses alternatives : virtualenv / conda.
|
|
||||||
|
|
||||||
|
|
||||||
## pip
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Jamais `sudo`, toujours dans un `venv`.
|
|
||||||
|
|
||||||
|
|
||||||
## pyproject.toml
|
|
||||||
|
|
||||||
- https://setuptools.readthedocs.io/
|
|
||||||
- https://github.com/JulienPalard/oeis
|
|
||||||
|
|
||||||
|
|
||||||
## pip install -e .
|
|
||||||
|
|
||||||
## Packager
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install build
|
|
||||||
python -m build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Publier
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install twine
|
|
||||||
twine upload dist/*
|
|
||||||
```
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
|
|
||||||
## 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 :
|
|
||||||
- flake8,
|
|
||||||
- pylint,
|
|
||||||
- mypy,
|
|
||||||
- black,
|
|
||||||
- bandit,
|
|
||||||
- isort,
|
|
||||||
- ruff,
|
|
||||||
- tox.
|
|
||||||
|
|
||||||
Notes: Leur faire implémenter un `is_prime(x)` pour jouer avec.
|
|
||||||
|
|
||||||
|
|
||||||
## pdb
|
|
||||||
|
|
||||||
```
|
|
||||||
breakpoint()
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## PYTHONDEVMODE=y
|
|
||||||
|
|
||||||
Et `./configure --with-pydebug`.
|
|
|
@ -1,230 +0,0 @@
|
||||||
# async / await
|
|
||||||
|
|
||||||
Une coroutine est une fonction dont l'exécution peut être suspendue.
|
|
||||||
|
|
||||||
|
|
||||||
## Callback Hell
|
|
||||||
|
|
||||||
```
|
|
||||||
function pong_handler(client)
|
|
||||||
{
|
|
||||||
client.on('data', function (data)
|
|
||||||
{
|
|
||||||
client.on('data_written', function ()
|
|
||||||
{
|
|
||||||
client.close()
|
|
||||||
});
|
|
||||||
client.write(data)
|
|
||||||
client.flush()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Avec des coroutines
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def pong_handler():
|
|
||||||
client.write(await client.read())
|
|
||||||
await client.flush()
|
|
||||||
client.close()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Les coroutines
|
|
||||||
|
|
||||||
- generator-based coroutines
|
|
||||||
- native coroutines
|
|
||||||
|
|
||||||
|
|
||||||
## Generator-based coroutines
|
|
||||||
|
|
||||||
```pytho
|
|
||||||
import types
|
|
||||||
|
|
||||||
|
|
||||||
@types.coroutine
|
|
||||||
def get_then_print(url):
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Native coroutines
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def get_then_print(url):
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Coroutines
|
|
||||||
|
|
||||||
Une `coroutine`, renvoie un objet `coroutine` :
|
|
||||||
|
|
||||||
```
|
|
||||||
>>> async def tum():
|
|
||||||
... print("tum")
|
|
||||||
...
|
|
||||||
>>> tum()
|
|
||||||
<coroutine object tum at 0x7fa294538468>
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Coroutines
|
|
||||||
|
|
||||||
```
|
|
||||||
>>> async def tum():
|
|
||||||
... print("tum")
|
|
||||||
...
|
|
||||||
>>> a_coroutine_object = tum()
|
|
||||||
>>> a_coroutine_object.send(None)
|
|
||||||
tum
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
StopIteration
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
qu'on peut manipuler.
|
|
||||||
|
|
||||||
As you can see, calling `tum()` did not execute the `print("tum")`,
|
|
||||||
but calling `.send(None)` did (see PEP 342).
|
|
||||||
|
|
||||||
L'appel de .send est fait par la main loop (asyncio.run).
|
|
||||||
|
|
||||||
|
|
||||||
## Récupérer un résultat
|
|
||||||
|
|
||||||
Le résultat d'une coroutine est stocké dans l'exception `StopIteration`.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Dans l'attribut `value`.
|
|
||||||
|
|
||||||
|
|
||||||
## await
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
async def two():
|
|
||||||
return 2
|
|
||||||
|
|
||||||
async def four():
|
|
||||||
return await two() + await two()
|
|
||||||
|
|
||||||
coro = four()
|
|
||||||
coro.send(None)
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Ça donne `StopIteration: 4`, de manière complètement synchrone.
|
|
||||||
|
|
||||||
|
|
||||||
## Suspendre une coroutine.
|
|
||||||
|
|
||||||
Ce n'est pas possible dans une coroutine.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Bon, à part `await asyncio.sleep(0)`, ou toute attente vers un
|
|
||||||
awaitable qui se suspend sans rien faire.
|
|
||||||
|
|
||||||
|
|
||||||
## Future-like object
|
|
||||||
|
|
||||||
Un `future-like object` est un object implémentant `__await__`, qui a
|
|
||||||
le droit de `yield`. L'expression du yield traversera toute la stack
|
|
||||||
d'`await` jusqu'au `send(None)`.
|
|
||||||
|
|
||||||
|
|
||||||
## Awaitables
|
|
||||||
|
|
||||||
Les [awaitables](https://www.python.org/dev/peps/pep-0492/#await-expression)
|
|
||||||
sont des objets pouvant être « attendus » via un `await`.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
Typiquement `coroutine` ou un objet implémentant `__await__`.
|
|
||||||
|
|
||||||
|
|
||||||
## Gérer ses coroutines
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def two():
|
|
||||||
return 2
|
|
||||||
|
|
||||||
async def four():
|
|
||||||
return await two() + await two()
|
|
||||||
|
|
||||||
def coro_manager(coro):
|
|
||||||
try:
|
|
||||||
coro.send(None)
|
|
||||||
except StopIteration as stop:
|
|
||||||
return stop.value
|
|
||||||
|
|
||||||
print(coro_manager(four()))
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Gérer ses coroutines
|
|
||||||
|
|
||||||
|
|
||||||
```python
|
|
||||||
class Awaitable:
|
|
||||||
def __await__(self):
|
|
||||||
yield
|
|
||||||
|
|
||||||
async def wont_terminate_here():
|
|
||||||
await Awaitable()
|
|
||||||
print("Terminated")
|
|
||||||
return 42
|
|
||||||
|
|
||||||
print(coro_manager(wont_terminate_here()))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Gérer ses coroutines
|
|
||||||
|
|
||||||
|
|
||||||
```python
|
|
||||||
def frenetic_coro_manager(coro):
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
coro.send(None)
|
|
||||||
except StopIteration as stop:
|
|
||||||
return stop.value
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Gérer ses coroutines
|
|
||||||
|
|
||||||
```python
|
|
||||||
import random
|
|
||||||
|
|
||||||
|
|
||||||
def frenetic_coros_manager(*coros):
|
|
||||||
coros = list(coros)
|
|
||||||
while coros:
|
|
||||||
coro = random.choice(coros)
|
|
||||||
try:
|
|
||||||
coro.send(None)
|
|
||||||
except StopIteration as stop:
|
|
||||||
coros.remove(coro)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Gérer ses coroutines
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def tum():
|
|
||||||
for _ in range(10): # ou : while True:
|
|
||||||
await Awaitable()
|
|
||||||
print("Tum")
|
|
||||||
|
|
||||||
async def pak():
|
|
||||||
for _ in range(10): # ou : while True:
|
|
||||||
await Awaitable()
|
|
||||||
print("Pak")
|
|
||||||
|
|
||||||
frenetic_coros_manager(tum(), pak())
|
|
||||||
```
|
|
|
@ -1,411 +0,0 @@
|
||||||
# Performance
|
|
||||||
|
|
||||||
## Le code
|
|
||||||
|
|
||||||
```python
|
|
||||||
def main():
|
|
||||||
already_checked = []
|
|
||||||
while True:
|
|
||||||
c = "".join(choice(ascii_letters) for _ in range(10))
|
|
||||||
if c in already_checked: continue
|
|
||||||
already_checked.append(c)
|
|
||||||
digest = sha512(
|
|
||||||
(c + args.string).encode("UTF-8")).hexdigest()
|
|
||||||
if digest.startswith(args.sha_prefix):
|
|
||||||
print(f"sha512({c} + {args.string}) = {digest}")
|
|
||||||
sys.exit(0)
|
|
||||||
print("Searching")
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Premiers tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ time python perf.py AFPy 00
|
|
||||||
Searching
|
|
||||||
[...]
|
|
||||||
Searching
|
|
||||||
Found: sha512(5NX3dB0BrO + AFPy) = 00…
|
|
||||||
|
|
||||||
real 0m0.048s
|
|
||||||
user 0m0.040s
|
|
||||||
sys 0m0.008s
|
|
||||||
```
|
|
||||||
|
|
||||||
## Premiers tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ time python perf.py AFPy 000
|
|
||||||
Searching
|
|
||||||
[...]
|
|
||||||
Searching
|
|
||||||
Found: sha512(UYb0z6nac1 + AFPy) = 000…
|
|
||||||
|
|
||||||
real 0m2.797s
|
|
||||||
user 0m2.773s
|
|
||||||
sys 0m0.024s
|
|
||||||
```
|
|
||||||
|
|
||||||
## Premiers tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ time python perf.py AFPy 0000
|
|
||||||
Searching
|
|
||||||
[...]
|
|
||||||
Searching
|
|
||||||
Found: sha512(dX0oAzvOmm + AFPy) = 0000…
|
|
||||||
|
|
||||||
real 0m16.381s
|
|
||||||
user 0m16.375s
|
|
||||||
sys 0m0.004s
|
|
||||||
```
|
|
||||||
|
|
||||||
C'est long mais ça passe …
|
|
||||||
|
|
||||||
|
|
||||||
## Premiers tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ time python perf.py AFPy 00000
|
|
||||||
Searching
|
|
||||||
[...]
|
|
||||||
Searching
|
|
||||||
Searching
|
|
||||||
Searching
|
|
||||||
Searching
|
|
||||||
```
|
|
||||||
|
|
||||||
Bon, on a un sushi.
|
|
||||||
|
|
||||||
## cProfile
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ python -m cProfile -o prof perf.py AFPy 0000
|
|
||||||
```
|
|
||||||
|
|
||||||
## pstats
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ python -m pstats prof
|
|
||||||
Welcome to the profile statistics browser.
|
|
||||||
prof% sort cumulative
|
|
||||||
prof% stats 10
|
|
||||||
```
|
|
||||||
|
|
||||||
## pstats
|
|
||||||
|
|
||||||
```txt
|
|
||||||
ncalls cumtime percall filename:lineno(function)
|
|
||||||
12/1 17.007 17.007 {built-in method builtins.exec}
|
|
||||||
1 17.007 17.007 /tmp/perf.py:1(<module>)
|
|
||||||
1 16.996 16.996 /tmp/perf.py:20(main)
|
|
||||||
36429 0.869 0.000 {method 'join' of 'str' objects}
|
|
||||||
```
|
|
||||||
|
|
||||||
## snakeviz
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pip install snakeviz
|
|
||||||
Collecting snakeviz
|
|
||||||
Using cached snakeviz-2.1.0-py2.py3-none-any.whl (282 kB)
|
|
||||||
Collecting tornado>=2.0
|
|
||||||
Using cached tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl (427 kB)
|
|
||||||
Installing collected packages: tornado, snakeviz
|
|
||||||
Successfully installed snakeviz-2.1.0 tornado-6.1
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## snakeviz
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ snakeviz prof
|
|
||||||
```
|
|
||||||
|
|
||||||
## snakeviz
|
|
||||||
![](static/snakeviz-v1.png)
|
|
||||||
|
|
||||||
|
|
||||||
## vprof
|
|
||||||
|
|
||||||
```
|
|
||||||
$ pip install vprof
|
|
||||||
Collecting vprof
|
|
||||||
Using cached vprof-0.38-py3-none-any.whl (319 kB)
|
|
||||||
Collecting psutil>=3
|
|
||||||
Using cached psutil-5.7.3-cp39-cp39d-linux_x86_64.whl
|
|
||||||
Installing collected packages: psutil, vprof
|
|
||||||
Successfully installed psutil-5.7.3 vprof-0.38
|
|
||||||
```
|
|
||||||
|
|
||||||
## vprof
|
|
||||||
|
|
||||||
```
|
|
||||||
$ vprof -c h "perf.py AFPy 0000"
|
|
||||||
```
|
|
||||||
|
|
||||||
## vprof
|
|
||||||
|
|
||||||
![](static/vprof.png)
|
|
||||||
|
|
||||||
|
|
||||||
## Le code, v1
|
|
||||||
|
|
||||||
```python [2,5,6]
|
|
||||||
def main():
|
|
||||||
already_checked = []
|
|
||||||
while True:
|
|
||||||
c = "".join(choice(ascii_letters) for _ in range(10))
|
|
||||||
if c in already_checked: continue
|
|
||||||
already_checked.append(c)
|
|
||||||
digest = sha512(
|
|
||||||
(c + args.string).encode("UTF-8")).hexdigest()
|
|
||||||
if digest.startswith(args.sha_prefix):
|
|
||||||
print(f"sha512({c} + {args.string}) = {digest}")
|
|
||||||
sys.exit(0)
|
|
||||||
print("Searching")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Le code, v2
|
|
||||||
|
|
||||||
```python [2,5,6]
|
|
||||||
def main():
|
|
||||||
already_checked = set()
|
|
||||||
while True:
|
|
||||||
c = "".join(choice(ascii_letters) for _ in range(10))
|
|
||||||
if c in already_checked: continue
|
|
||||||
already_checked.add(c)
|
|
||||||
digest = sha512(
|
|
||||||
(c + args.string).encode("UTF-8")).hexdigest()
|
|
||||||
if digest.startswith(args.sha_prefix):
|
|
||||||
print(f"sha512({c} + {args.string}) = {digest}")
|
|
||||||
sys.exit(0)
|
|
||||||
print("Searching")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Les perfs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ hyperfine 'python perf.py AFPy 00000'
|
|
||||||
```
|
|
||||||
- v1 : ∞
|
|
||||||
- v2 (`set`) : 23 s ± 23 s
|
|
||||||
|
|
||||||
::: notes
|
|
||||||
|
|
||||||
Il existe aussi pyperf: https://github.com/psf/pyperf
|
|
||||||
|
|
||||||
|
|
||||||
## cProfile + pstats
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ python -m cProfile -o prof perf.py AFPy 0000
|
|
||||||
$ python -m pstats prof
|
|
||||||
```
|
|
||||||
|
|
||||||
## cProfile + pstats
|
|
||||||
```
|
|
||||||
ncalls cumtime percall filename:lineno(function)
|
|
||||||
12/1 1.156 1.156 {built-in method builtins.exec}
|
|
||||||
1 1.156 1.156 perf.py:1(<module>)
|
|
||||||
1 1.143 1.143 perf.py:35(main)
|
|
||||||
34215 0.771 0.000 {method 'join' of 'str' objects}
|
|
||||||
371647 0.681 0.000 perf.py:39(<genexpr>)
|
|
||||||
337860 0.526 0.000 /python3.9/random.py(choice)
|
|
||||||
337860 0.283 0.000 /python3.9/random.py(randbelow)
|
|
||||||
33786 0.134 0.000 built-in method print
|
|
||||||
372745 0.037 0.000 method 'getrandbits' of Random'
|
|
||||||
33786 0.037 0.000 method 'hexdigest' of hashlib
|
|
||||||
```
|
|
||||||
|
|
||||||
## snakeviz
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ snakeviz prof
|
|
||||||
```
|
|
||||||
|
|
||||||
## snakeviz
|
|
||||||
|
|
||||||
![](static/snakeviz-v2.png)
|
|
||||||
|
|
||||||
|
|
||||||
## Le code, v2
|
|
||||||
|
|
||||||
```python [4]
|
|
||||||
def main():
|
|
||||||
already_checked = set()
|
|
||||||
while True:
|
|
||||||
c = "".join(choice(ascii_letters) for _ in range(10))
|
|
||||||
if c in already_checked: continue
|
|
||||||
already_checked.add(c)
|
|
||||||
digest = sha512(
|
|
||||||
(c + args.string).encode("UTF-8")).hexdigest()
|
|
||||||
if digest.startswith(args.sha_prefix):
|
|
||||||
print(f"sha512({c} + {args.string}) = {digest}")
|
|
||||||
sys.exit(0)
|
|
||||||
print("Searching")
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Le code, v3
|
|
||||||
|
|
||||||
```python [4]
|
|
||||||
def main():
|
|
||||||
already_checked = set()
|
|
||||||
while True:
|
|
||||||
c = "".join(choices(ascii_letters, k=10))
|
|
||||||
if c in already_checked: continue
|
|
||||||
already_checked.add(c)
|
|
||||||
digest = sha512(
|
|
||||||
(c + args.string).encode("UTF-8")).hexdigest()
|
|
||||||
if digest.startswith(args.sha_prefix):
|
|
||||||
print(f"sha512({c} + {args.string}) = {digest}")
|
|
||||||
sys.exit(0)
|
|
||||||
print("Searching")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Les perfs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ hyperfine 'python perf.py AFPy 00000'
|
|
||||||
```
|
|
||||||
- v1 : ∞
|
|
||||||
- v2 (`set`) : 23 s ± 23 s
|
|
||||||
- v3 (`choices`): 8.591 s ± 6.525 s
|
|
||||||
|
|
||||||
|
|
||||||
## snakeviz
|
|
||||||
|
|
||||||
![](static/snakeviz-v3.png)
|
|
||||||
|
|
||||||
|
|
||||||
## Le code, v4
|
|
||||||
|
|
||||||
```python [3]
|
|
||||||
def main():
|
|
||||||
already_checked = set()
|
|
||||||
for c in product(ascii_letters, repeat=10):
|
|
||||||
c = "".join(c)
|
|
||||||
if c in already_checked: continue
|
|
||||||
already_checked.add(c)
|
|
||||||
digest = sha512(
|
|
||||||
(c + args.string).encode("UTF-8")).hexdigest()
|
|
||||||
if digest.startswith(args.sha_prefix):
|
|
||||||
print(f"sha512({c} + {args.string}) = {digest}")
|
|
||||||
sys.exit(0)
|
|
||||||
print("Searching")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Les perfs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ hyperfine 'python perf.py AFPy 00000'
|
|
||||||
```
|
|
||||||
- v1 : ∞
|
|
||||||
- v2 (`set`) : 23 s ± 23 s
|
|
||||||
- v3 (`choices`): 8.591 s ± 6.525 s
|
|
||||||
- v4 (`deterministic`) : 3.900 s ± 0.121 s
|
|
||||||
|
|
||||||
|
|
||||||
## snakeviz
|
|
||||||
|
|
||||||
![](static/snakeviz-v4.png)
|
|
||||||
|
|
||||||
|
|
||||||
## Le code, v5
|
|
||||||
|
|
||||||
```python [12]
|
|
||||||
def main():
|
|
||||||
already_checked = set()
|
|
||||||
for c in product(ascii_letters, repeat=10):
|
|
||||||
c = "".join(c)
|
|
||||||
if c in already_checked: continue
|
|
||||||
already_checked.add(c)
|
|
||||||
digest = sha512(
|
|
||||||
(c + args.string).encode("UTF-8")).hexdigest()
|
|
||||||
if digest.startswith(args.sha_prefix):
|
|
||||||
print(f"sha512({c} + {args.string}) = {digest}")
|
|
||||||
sys.exit(0)
|
|
||||||
# print("Searching")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Les perfs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ hyperfine 'python perf.py AFPy 00000'
|
|
||||||
```
|
|
||||||
- v1 : ∞
|
|
||||||
- v2 (`set`) : 23 s ± 23 s
|
|
||||||
- v3 (`choices`): 8.591 s ± 6.525 s
|
|
||||||
- v4 (`deterministic`) : 3.900 s ± 0.121 s
|
|
||||||
- v5 (`print`) : 3.120 s ± 0.062 s
|
|
||||||
|
|
||||||
|
|
||||||
## Snakeviz
|
|
||||||
|
|
||||||
![](static/snakeviz-v5.png)
|
|
||||||
|
|
||||||
Il reste du `hexdigest`, du `encode`, et du `join`.
|
|
||||||
|
|
||||||
## vprof
|
|
||||||
|
|
||||||
![](static/vprof2.png)
|
|
||||||
|
|
||||||
Ligne 26 et 28 !?
|
|
||||||
|
|
||||||
## Le code, v6
|
|
||||||
|
|
||||||
```python
|
|
||||||
def main():
|
|
||||||
for c in product(ascii_letters, repeat=10):
|
|
||||||
c = "".join(c)
|
|
||||||
digest = sha512(
|
|
||||||
(c + args.string).encode("UTF-8")).hexdigest()
|
|
||||||
if digest.startswith(args.sha_prefix):
|
|
||||||
print(f"sha512({c} + {args.string}) = {digest}")
|
|
||||||
sys.exit(0)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Snakeviz
|
|
||||||
|
|
||||||
![](static/snakeviz-v6.png)
|
|
||||||
|
|
||||||
Il reste du `hexdigest`, du `encode`, et du `join`.
|
|
||||||
|
|
||||||
|
|
||||||
## Le code, v7
|
|
||||||
|
|
||||||
```python
|
|
||||||
def main():
|
|
||||||
string = args.string.encode("UTF-8")
|
|
||||||
pool = ascii_letters.encode("UTF-8")
|
|
||||||
for c in product(pool, repeat=10):
|
|
||||||
digest = sha512(bytes(c) + string).hexdigest()
|
|
||||||
if digest.startswith(args.sha_prefix):
|
|
||||||
print(f"sha512({bytes(c)} + {args.string}) = "
|
|
||||||
f"{digest}")
|
|
||||||
sys.exit(0)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Les perfs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ hyperfine 'python perf.py AFPy 00000'
|
|
||||||
```
|
|
||||||
- v1 : ∞
|
|
||||||
- v2 (`set`) : 23 s ± 23 s
|
|
||||||
- v3 (`choices`): 8.591 s ± 6.525 s
|
|
||||||
- v4 (`deterministic`) : 3.900 s ± 0.121 s
|
|
||||||
- v5 (`print`) : 3.120 s ± 0.062 s
|
|
||||||
- v6 (`dead code`): 2.844 s ± 0.059 s
|
|
||||||
- v7 (`bytes`) : 1.837 s ± 0.067 s
|
|
||||||
|
|
||||||
|
|
||||||
## Encore plus d'expériences
|
|
||||||
|
|
||||||
- pypy: 3.8s
|
|
||||||
- python: 1.8s
|
|
||||||
- cython (hashlib) 1.3s
|
|
||||||
- cython (crypto) 0.8s
|
|
||||||
- c: 0.3s
|
|
1641
python-avancé/python-avancé.md
Normal file
1641
python-avancé/python-avancé.md
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user