Compare commits
10 Commits
c2d062d445
...
ef38c95a90
Author | SHA1 | Date |
---|---|---|
Julien Palard | ef38c95a90 | |
Julien Palard | 58bc529a43 | |
Julien Palard | 263ae02153 | |
Julien Palard | cecadcf4bc | |
Julien Palard | 313092b0d9 | |
Julien Palard | 7497d56e53 | |
Julien Palard | e37af50649 | |
Julien Palard | 596e9c5ae1 | |
Julien Palard | 7aa7335835 | |
Julien Palard | 44270684fe |
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
|
||||
name: Publish
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: ${{ matrix.directory }}
|
||||
strategy:
|
||||
matrix:
|
||||
directory:
|
||||
- python-initiation
|
||||
- python-avancé
|
||||
- drf-initiation
|
||||
- django-initiation
|
||||
- python-perfs
|
||||
- git-initiation
|
||||
secrets:
|
||||
deploy_key: ${{ secrets.deploy_key }}
|
||||
known_hosts: ${{ secrets.known_hosts }}
|
||||
uses: JulienPalard/formations/.github/workflows/deploy.yml@main
|
||||
with:
|
||||
directory: ${{ matrix.directory }}
|
|
@ -1,42 +0,0 @@
|
|||
---
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
directory:
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
deploy_key:
|
||||
required: true
|
||||
known_hosts:
|
||||
required: true
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
cache: pip
|
||||
- run: sudo apt-get install -y cutycapt
|
||||
- run: python3 -m pip install -r requirements.txt
|
||||
- run: make -C ${{ inputs.directory }} test
|
||||
- name: Publish
|
||||
env:
|
||||
deploy_key: ${{secrets.deploy_key}}
|
||||
known_hosts: ${{secrets.known_hosts}}
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
run: |
|
||||
mkdir -p ${HOME}/.ssh
|
||||
printf "%s\n" "$known_hosts" > ${HOME}/.ssh/known_hosts
|
||||
printf "%s\n" "$deploy_key" > ${HOME}/.ssh/id_ed25519
|
||||
chmod 600 ${HOME}/.ssh/id_ed25519
|
||||
eval $(ssh-agent)
|
||||
ssh-add
|
||||
rm ${HOME}/.ssh/id_ed25519
|
||||
export deploy_key=""
|
||||
export PATH="$HOME/.local/bin/:$PATH"
|
||||
cd ${{ inputs.directory }}
|
||||
make rsync
|
2
Makefile
2
Makefile
|
@ -3,7 +3,7 @@ DEST := $(notdir $(PWD))
|
|||
|
||||
.PHONY: static
|
||||
static: output/index.html
|
||||
if [ -d static ]; then cp -a static/ output/; fi
|
||||
if [ -d static ]; then cp -rpL static/ output/; fi
|
||||
|
||||
%.html: %.md
|
||||
sed 's/#!//e;' $< | mdtoreveal /dev/stdin --output $@
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
# GNU/Linux avec Debian
|
||||
|
||||
## Présenté
|
||||
### Présenté
|
||||
|
||||
<!-- .slide: data-background="static/background.jpg" -->
|
||||
|
||||
par
|
||||
|
||||
Julien Palard <julien@palard.fr>
|
||||
|
||||
|
||||
https://mdk.fr
|
||||
|
||||
::: notes
|
||||
|
@ -29,7 +32,7 @@ Introduce yourself!
|
|||
|
||||
## KDE
|
||||
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d9/KDE_1.0.png" style="width: 80%"/>
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d9/KDE_1.0.png" style="width: 70%"/>
|
||||
|
||||
## KDE
|
||||
|
||||
|
@ -46,7 +49,19 @@ Introduce yourself!
|
|||
|
||||
# Le shell
|
||||
|
||||
![](static/shell.png)
|
||||
```shell
|
||||
mdk@seraph$ date
|
||||
Thu May 25 03:37:25 PM CEST 2023
|
||||
|
||||
mdk@seraph$ cal
|
||||
May 2023
|
||||
Su Mo Tu We Th Fr Sa
|
||||
1 2 3 4 5 6
|
||||
7 8 9 10 11 12 13
|
||||
14 15 16 17 18 19 20
|
||||
21 22 23 24 25 26 27
|
||||
28 29 30 31
|
||||
```
|
||||
|
||||
## Les commandes
|
||||
|
||||
|
@ -59,7 +74,7 @@ Ces programmes peuvent prendre des « arguments » ou des « paramètres ».
|
|||
|
||||
Dans :
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ date
|
||||
```
|
||||
|
||||
|
@ -76,7 +91,7 @@ avec backspace.
|
|||
|
||||
Dans :
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ man intro
|
||||
```
|
||||
|
||||
|
@ -91,15 +106,15 @@ Mais pourquoi !?
|
|||
|
||||
## Le shell
|
||||
|
||||
- C'est plus rapide.
|
||||
- Ça permet de faire collaborer plusieurs programmes.
|
||||
- C'est à la portée de tout développeur d'ajouter un programme en quelques minutes.
|
||||
- Ça permet de faire collaborer des programmes.
|
||||
- Il est facile d'ajouter des programmes.
|
||||
- Tout au clavier, c'est plus rapide.
|
||||
|
||||
|
||||
## C'est plus rapide
|
||||
|
||||
- Dû à la simplicité de l'interface.
|
||||
- L'auto-complétion des commandes et parfois de leurs arguments.
|
||||
- L'auto-complétion des commandes.
|
||||
- Les raccourcis claviers.
|
||||
- La quantité de programmes disponnibles (~4500 chez moi, là).
|
||||
|
||||
|
@ -184,18 +199,20 @@ Digrésser sur Markdown et reStructuredText, mentionner groff.
|
|||
|
||||
Voici les programmes que j'utilise le plus :
|
||||
|
||||
$ history | awk '{print $2}' | sort | uniq -c | sort -gr
|
||||
3408 git
|
||||
902 cd
|
||||
765 python
|
||||
539 ls
|
||||
385 emacs
|
||||
330 curl
|
||||
327 ssh
|
||||
291 make
|
||||
209 mv
|
||||
188 rm
|
||||
...
|
||||
```shell
|
||||
$ history | awk '{print $2}' | sort | uniq -c | sort -gr
|
||||
3408 git
|
||||
902 cd
|
||||
765 python
|
||||
539 ls
|
||||
385 emacs
|
||||
330 curl
|
||||
327 ssh
|
||||
291 make
|
||||
209 mv
|
||||
188 rm
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## Démo
|
||||
|
@ -210,7 +227,7 @@ C'est un fichier de produits alimentaires.
|
|||
|
||||
Téléchargeons et décompressons le fichier :
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ curl -O https://fr.openfoodfacts.org/data/fr.openfoodfacts.org.products.csv.gz
|
||||
$ gunzip fr.openfoodfacts.org.products.csv.gz
|
||||
$ mv fr.openfoodfacts.org.products.csv products.csv
|
||||
|
@ -223,7 +240,19 @@ Attention, compressé il fait ~700M, décompressé ~8GB.
|
|||
|
||||
Peser le fichier :
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ du -h products.csv
|
||||
8103812 products.csv
|
||||
```
|
||||
|
||||
Ahh oui, 8 millions d'octets…
|
||||
|
||||
|
||||
## Démo
|
||||
|
||||
Plus **h**umain :
|
||||
|
||||
```shell
|
||||
$ du -h products.csv
|
||||
7.8G products.csv
|
||||
```
|
||||
|
@ -233,7 +262,7 @@ $ du -h products.csv
|
|||
|
||||
Compter les lignes :
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ wc -l products.csv
|
||||
2864834 products.csv
|
||||
```
|
||||
|
@ -241,12 +270,11 @@ $ wc -l products.csv
|
|||
|
||||
## Démo
|
||||
|
||||
Obtenir des informations sur le contenu du fichier :
|
||||
Que contient-il ?
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ file products.csv
|
||||
products.csv: UTF-8 Unicode text,
|
||||
with very long lines
|
||||
products.csv: UTF-8 Unicode text, with very long lines
|
||||
```
|
||||
|
||||
|
||||
|
@ -254,7 +282,7 @@ with very long lines
|
|||
|
||||
Lire le fichier :
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ less products.csv
|
||||
```
|
||||
|
||||
|
@ -265,7 +293,7 @@ Attention le fichier est gigantesque !
|
|||
|
||||
Obtenir les 10 premières lignes du fichier :
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ head products.csv
|
||||
code url creator created_t created_datetime ... ...
|
||||
00000000000000225 http://world-fr.openfoodfacts.org/produit/00000000000000225/jeunes-pousses-endives...
|
||||
|
@ -277,7 +305,7 @@ code url creator created_t created_datetime ... ...
|
|||
## Les noms des colonnes
|
||||
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ head -n 1 products.csv | tr '\t' '\n'
|
||||
code
|
||||
url
|
||||
|
@ -288,20 +316,18 @@ created_datetime
|
|||
|
||||
## Le nombre de colonnes
|
||||
|
||||
```bash
|
||||
$ head -n 1 products.csv |
|
||||
> tr '\t' '\n' | wc -l
|
||||
```shell
|
||||
$ head -n 1 products.csv | tr '\t' '\n' | wc -l
|
||||
201
|
||||
```
|
||||
|
||||
Parmis les colonnes je repère `additives`...
|
||||
Parmis les colonnes je repère `additives`…
|
||||
|
||||
|
||||
## Les numéros des colonnes
|
||||
|
||||
```bash
|
||||
$ head -n 1 products.csv |
|
||||
> tr '\t' '\n'| grep -n additives
|
||||
```shell
|
||||
$ head -n 1 products.csv | tr '\t' '\n' | grep -n additives
|
||||
51:additives_n
|
||||
52:additives
|
||||
53:additives_tags
|
||||
|
@ -310,17 +336,17 @@ $ head -n 1 products.csv |
|
|||
|
||||
## Les additifs
|
||||
|
||||
```bash
|
||||
cut -f 51-54 products.csv | less
|
||||
```shell
|
||||
$ cut -f 51-54 products.csv | less
|
||||
```
|
||||
|
||||
Je repère « E412 », je me demande si beaucoup de produits l'utilisent…
|
||||
|
||||
|
||||
## Les additifs
|
||||
|
||||
```bash
|
||||
$ cut -f 51-54 products.csv |
|
||||
> grep -c E412
|
||||
```shell
|
||||
$ cut -f 51-54 products.csv | grep -c E412
|
||||
44554
|
||||
```
|
||||
|
||||
|
@ -329,7 +355,7 @@ Je me demande quel est l'additif le plus utilisé…
|
|||
|
||||
## Les additifs
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ cut -f 51-54 products.csv |
|
||||
> grep -oE 'E[0-9]{3}' | sort | uniq -c | sort -gr | head
|
||||
196667 E322
|
||||
|
@ -345,6 +371,38 @@ $ cut -f 51-54 products.csv |
|
|||
```
|
||||
|
||||
|
||||
# Le système de fichiers
|
||||
|
||||
> Tout est fichier.
|
||||
|
||||
```shell
|
||||
$ ls /
|
||||
bin home lib32 media root sys vmlinuz
|
||||
boot initrd.img lib64 mnt run tmp
|
||||
dev initrd.img.old libx32 opt sbin usr
|
||||
etc lib lost+found proc srv var
|
||||
```
|
||||
|
||||
ou
|
||||
|
||||
```shell
|
||||
$ man hier
|
||||
```
|
||||
|
||||
|
||||
## Même l'alimentation électrique
|
||||
|
||||
```shell
|
||||
$ cat /sys/class/power_supply/AC/online
|
||||
1
|
||||
```
|
||||
|
||||
## Les droits
|
||||
|
||||
|
||||
|
||||
## Les points de montage
|
||||
|
||||
# ssh
|
||||
|
||||
SSH: Parler de clefs, conseiller ED25519
|
||||
|
@ -485,9 +543,6 @@ Quelques démos s'imposeront avec `iconv` et peut être Python.
|
|||
|
||||
# TODO
|
||||
|
||||
- Le système de fichiers
|
||||
- Les droits
|
||||
- Les montages
|
||||
- Petit historique
|
||||
- Les window-managers
|
||||
- Sweet sweet thinks like youtube-dl
|
||||
|
|
|
@ -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,52 +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/*
|
||||
```
|
|
@ -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
|
|
@ -0,0 +1,28 @@
|
|||
# This is the creation « from scratch » of the following class:
|
||||
|
||||
# class Foo:
|
||||
# def say(self):
|
||||
# print("hello")
|
||||
|
||||
# First the type.__prepare__ is called to create a namespace, it
|
||||
# typically returns a fresh empty dict:
|
||||
|
||||
ns = {}
|
||||
|
||||
# Then the class body is executed in this namespace:
|
||||
|
||||
exec(
|
||||
"""
|
||||
def say(self):
|
||||
print("hello")
|
||||
""",
|
||||
globals(),
|
||||
ns,
|
||||
)
|
||||
|
||||
# Now, the type __new__ is called to create the class:
|
||||
|
||||
Foo = type.__new__(type, "Foo", (), ns)
|
||||
type.__init__(Foo, "Foo", (), ns) # Does nothing
|
||||
|
||||
Foo().say()
|
|
@ -0,0 +1,39 @@
|
|||
from urllib.parse import parse_qsl
|
||||
|
||||
|
||||
class Application:
|
||||
def __init__(self):
|
||||
self.routes = {}
|
||||
|
||||
def route(self, path):
|
||||
def deco(fct):
|
||||
self.routes[path] = fct
|
||||
return fct
|
||||
return deco
|
||||
|
||||
def get(self, path):
|
||||
try:
|
||||
path, qs = path.split("?", maxsplit=1)
|
||||
except ValueError:
|
||||
qs = {}
|
||||
return self.routes[path](**dict(parse_qsl(qs)))
|
||||
|
||||
app = Application()
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def home():
|
||||
return "<a href='/api'>/api</a>"
|
||||
|
||||
@app.route("/api")
|
||||
def api():
|
||||
return {"version": "1"}
|
||||
|
||||
@app.route("/auth/")
|
||||
def auth(username, password):
|
||||
return f"Wrong password for {username}"
|
||||
|
||||
|
||||
print(app.get("/"))
|
||||
print(app.get("/api"))
|
||||
print(app.get("/auth/?username=mdk&password=test"))
|
|
@ -0,0 +1,19 @@
|
|||
def memoize(limit): # Factory
|
||||
def _memoize(function_fib):
|
||||
memory = {}
|
||||
def cached_fib(n):
|
||||
if n in memory:
|
||||
return memory[n]
|
||||
result = function_fib(n)
|
||||
memory[n] = result
|
||||
return result
|
||||
return cached_fib
|
||||
return _memoize
|
||||
|
||||
@memoize(limit=1024)
|
||||
def fib(n):
|
||||
if n < 2:
|
||||
return 1
|
||||
return fib(n-1) + fib(n-2)
|
||||
|
||||
# fib = memoize(limit=1024)(fib)
|
|
@ -0,0 +1,33 @@
|
|||
class PositiveInteger:
|
||||
def __set_name__(self, owner, name):
|
||||
self.name = name
|
||||
|
||||
def __get__(self, instance, cls=None):
|
||||
if instance is None:
|
||||
return self
|
||||
return instance.__dict__[self.name]
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if value < 0:
|
||||
raise ValueError("Negative values are not acceptable.")
|
||||
instance.__dict__[self.name] = value
|
||||
|
||||
|
||||
class Order:
|
||||
qty = PositiveInteger()
|
||||
price = PositiveInteger()
|
||||
|
||||
def __init__(self, item, price, qty):
|
||||
self.item = item
|
||||
self.price = price
|
||||
self.qty = qty
|
||||
|
||||
def cost(self):
|
||||
return self.price * self.qty
|
||||
|
||||
|
||||
# Order("un truc", -1, 1)
|
||||
# o = Order("Un livre", 35, 1)
|
||||
# o.qty += 1
|
||||
# o.qty -= 10
|
||||
# print(o.cost())
|
|
@ -0,0 +1,14 @@
|
|||
from string import ascii_letters
|
||||
|
||||
from hypothesis import given, settings
|
||||
|
||||
from hypothesis.strategies import text, lists
|
||||
|
||||
|
||||
@given(lists(text(alphabet=ascii_letters, max_size=10)))
|
||||
@settings(max_examples=500)
|
||||
def test_sort_is_stable(a_list):
|
||||
print(a_list)
|
||||
double_sort = sorted(sorted(a_list, key=str.lower), key=len)
|
||||
single_sort = sorted(a_list, key=lambda s: (len(s), s.lower()))
|
||||
assert double_sort == single_sort
|
|
@ -0,0 +1,27 @@
|
|||
class AutoInit(type):
|
||||
def __new__(mcs, name, bases, ns, **kwargs):
|
||||
cls = type.__new__(mcs, name, bases, ns)
|
||||
cls._attributes = kwargs["attributes"]
|
||||
return cls
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
attributes = {}
|
||||
for name in self._attributes:
|
||||
attributes[name] = kwargs.pop(name, None)
|
||||
obj = super().__call__(*args, **kwargs)
|
||||
for key, value in attributes.items():
|
||||
setattr(obj, key, value)
|
||||
return obj
|
||||
|
||||
|
||||
class Point(metaclass=AutoInit, attributes=("x", "y", "z")):
|
||||
...
|
||||
|
||||
|
||||
origin = Point(x=0, y=0, z=0)
|
||||
print(origin.x, origin.y, origin.z) # 0 0 0
|
||||
|
||||
NonePoint = Point()
|
||||
print(NonePoint.x, NonePoint.y) # None None
|
||||
|
||||
# origin = Point(x=0, y=0, blah=42) # TypeError !
|
|
@ -0,0 +1,66 @@
|
|||
from functools import wraps
|
||||
from time import perf_counter, sleep
|
||||
from typing import Self
|
||||
|
||||
|
||||
def as_call(args, kwargs):
|
||||
passed = ['self']
|
||||
for arg in args[1:]:
|
||||
passed.append(repr(arg))
|
||||
for key, value in kwargs.items():
|
||||
passed.append(f"key={value!r}")
|
||||
return "(" + ", ".join(passed) + ")"
|
||||
|
||||
|
||||
def introspected_method(clsname, ctx, name, method):
|
||||
def _(*args, **kwargs):
|
||||
callstr = f"{clsname}.{name}{as_call(args, kwargs)}"
|
||||
print(f"{'| ' * ctx['depth']}Calling {callstr}...")
|
||||
ctx["depth"] += 1
|
||||
before = perf_counter()
|
||||
result = method(*args, **kwargs)
|
||||
after = perf_counter()
|
||||
ctx["depth"] -= 1
|
||||
callstr = f"{clsname}.{name}{as_call(args, kwargs)}"
|
||||
print(
|
||||
f"{'| ' * ctx['depth']}Returning from {callstr} after {after-before:.2f}s with {result}"
|
||||
)
|
||||
return result
|
||||
|
||||
return _
|
||||
|
||||
|
||||
class Introspected(type):
|
||||
def __new__(cls, name, bases, ns, **kwds):
|
||||
ctx = {"depth": 0}
|
||||
for key, value in ns.items():
|
||||
if callable(value) and key != "__repr__":
|
||||
ns[key] = introspected_method(name, ctx, key, value)
|
||||
return super().__new__(cls, name, bases, ns, **kwds)
|
||||
|
||||
|
||||
class Point(metaclass=Introspected):
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def dist(self, other: Self) -> float:
|
||||
return ((other.x - self.x) ** 2 + (other.y - self.y) ** 2) ** 0.5
|
||||
|
||||
def __repr__(self):
|
||||
return f"Point(x={self.x!r}, y={self.y!r})"
|
||||
|
||||
def modulus(self):
|
||||
return self.dist(Point(0, 0))
|
||||
|
||||
def recursive_call(self, value):
|
||||
sleep(0.1)
|
||||
if value:
|
||||
return self.recursive_call(value - 1)
|
||||
else:
|
||||
return self.mean()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
p = Point(10, 10)
|
||||
print(p.modulus())
|
|
@ -0,0 +1,26 @@
|
|||
class StronglyTypedMeta(type):
|
||||
def __new__(cls, name, bases, ns, **kwds):
|
||||
if '__annotations__' in ns:
|
||||
for name, type in ns['__annotations__'].items():
|
||||
ns[name] = StronglyTypedDescriptor(name, type)
|
||||
return super().__new__(cls, name, bases, ns, **kwds)
|
||||
|
||||
|
||||
class StronglyTypedDescriptor:
|
||||
def __init__(self, name, type):
|
||||
self.name = name
|
||||
self.type = type
|
||||
|
||||
def __get__(self, instance, cls=None):
|
||||
return instance.__dict__[self.name]
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if not isinstance(value, self.type):
|
||||
raise TypeError(f"{self.name} should be of type {self.type}.")
|
||||
instance.__dict__[self.name] = value
|
||||
|
||||
|
||||
|
||||
class Test(metaclass=StronglyTypedMeta):
|
||||
x: str
|
||||
y: int
|
|
@ -0,0 +1,70 @@
|
|||
class Meta(type):
|
||||
@classmethod
|
||||
def __prepare__(metacls, name, bases, **kwds):
|
||||
"""Prepare the namespace in which the class will be executed."""
|
||||
print(
|
||||
"Meta.__prepare__(",
|
||||
f"{metacls=},",
|
||||
f"{name=},",
|
||||
f"{bases=},",
|
||||
f"{kwds=}) -> {{}}",
|
||||
sep="\n ",
|
||||
)
|
||||
return {}
|
||||
|
||||
# __new__() is a static method (special-cased so you need not declare it as such)
|
||||
def __new__(cls, name, bases, namespace, **kwds):
|
||||
"""The class body is executed in a new namespace and the class name is
|
||||
bound locally to the result of type(name, bases, namespace).
|
||||
"""
|
||||
print(
|
||||
"\nMeta.__new__(",
|
||||
f"{cls=},",
|
||||
f"{name=},",
|
||||
f"{bases=},",
|
||||
f"{namespace=}",
|
||||
f"{kwds=})",
|
||||
sep="\n ",
|
||||
end=" ",
|
||||
)
|
||||
returns = type.__new__(cls, name, bases, namespace)
|
||||
print(f"-> {returns}\n")
|
||||
return returns
|
||||
|
||||
def __init__(self, name, bases, namespace, **kwds):
|
||||
"""This is the actual creation of the class !
|
||||
|
||||
As a class is an instance of its type."""
|
||||
print(
|
||||
"Meta.__init__(",
|
||||
f"{self=},",
|
||||
f"{name=},",
|
||||
f"{bases=},",
|
||||
f"{namespace=},",
|
||||
f"{kwds=})",
|
||||
sep="\n ",
|
||||
end="\n\n"
|
||||
)
|
||||
|
||||
|
||||
class Classe(metaclass=Meta):
|
||||
|
||||
print("\nAfter Meta.__prepare__ is called, the interpreter executes the class body "
|
||||
"like:")
|
||||
print(" exec(body, globals(), ns)")
|
||||
print("with `ns` the dict returned from __prepare__")
|
||||
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
print(f"Classe.__new__({cls=}, {args=}, {kwargs=})")
|
||||
returns = object.__new__(cls)
|
||||
print(f" returns {returns}")
|
||||
return returns
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
print(f"Classe.__init__({self=}, {args=}, {kwargs=})")
|
||||
|
||||
|
||||
print("Will now instantiate a Classe()")
|
||||
|
||||
Classe()
|
|
@ -0,0 +1,36 @@
|
|||
def introspected_method(clsname, name, method):
|
||||
def _(*args, **kwargs):
|
||||
print(f"Calling {clsname}.{name}(*{args!r}, **{kwargs!r})...")
|
||||
returning = method(*args, **kwargs)
|
||||
print(f" returned {returning!r}")
|
||||
return returning
|
||||
|
||||
return _
|
||||
|
||||
|
||||
class Introspected(type):
|
||||
def __new__(cls, name, bases, ns, **kwds):
|
||||
for key, value in ns.items():
|
||||
if isinstance(value, classmethod):
|
||||
ns[key] = classmethod(introspected_method(name, key, value.__wrapped__))
|
||||
elif callable(value) and key != "__repr__":
|
||||
ns[key] = introspected_method(name, key, value)
|
||||
return super().__new__(cls, name, bases, ns, **kwds)
|
||||
|
||||
|
||||
class Metaclass(type, metaclass=Introspected):
|
||||
@classmethod
|
||||
def __prepare__(metacls, name, bases, **kwds):
|
||||
return super().__prepare__(metacls, name, bases, **kwds)
|
||||
|
||||
def __new__(cls, name, bases, ns, **kwds):
|
||||
return super().__new__(cls, name, bases, ns, **kwds)
|
||||
|
||||
def __init__(self, name, bases, ns, **kwds):
|
||||
super().__init__(name, bases, ns, **kwds)
|
||||
|
||||
|
||||
class Point(metaclass=Metaclass):
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
|
@ -0,0 +1,44 @@
|
|||
from pathlib import Path
|
||||
|
||||
class Context:
|
||||
def __init__(self):
|
||||
print("Constructing context")
|
||||
|
||||
def __enter__(self):
|
||||
print("Entering context")
|
||||
return "Coucou"
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
print("Exiting context", exc_type)
|
||||
|
||||
# with Context() as ctx:
|
||||
# print("In context", ctx)
|
||||
|
||||
|
||||
from tempfile import NamedTemporaryFile
|
||||
import os
|
||||
|
||||
class InplaceEdit:
|
||||
def __init__(self, filename, encoding="UTF-8"):
|
||||
self.filename = filename
|
||||
self.ifile = open(filename, encoding=encoding)
|
||||
self.encoding = encoding
|
||||
self.tmpfile = NamedTemporaryFile(mode="w", delete=False, encoding=encoding, dir=Path(filename).parent)
|
||||
|
||||
def __enter__(self):
|
||||
return self.ifile, self.tmpfile
|
||||
|
||||
def __exit__(self, typ, value, tb):
|
||||
self.ifile.close()
|
||||
self.tmpfile.close()
|
||||
if typ is None:
|
||||
os.rename(self.tmpfile.name, self.filename)
|
||||
else:
|
||||
os.unlink(self.tmpfile.name)
|
||||
|
||||
# with open("/etc/hosts") as hosts, open("/etc/shadow") as shadow:
|
||||
# ...
|
||||
|
||||
with InplaceEdit("hosts.txt") as (ifile, ofile):
|
||||
for line in ifile:
|
||||
ofile.write(line.replace("www.mdk.fr", "mdk.fr"))
|
|
@ -1,26 +0,0 @@
|
|||
digraph Python {
|
||||
subgraph cluster_0 {
|
||||
node [style=filled];
|
||||
label = "Names";
|
||||
color = lightgrey;
|
||||
un;
|
||||
deux;
|
||||
trois;
|
||||
liste;
|
||||
}
|
||||
subgraph cluster_1 {
|
||||
node [style=filled];
|
||||
label = "Values";
|
||||
color = lightgrey;
|
||||
1;
|
||||
2;
|
||||
3;
|
||||
4;
|
||||
"[...]" -> 1;
|
||||
"[...]" -> 2;
|
||||
}
|
||||
un -> 1;
|
||||
deux -> 2;
|
||||
trois -> 3;
|
||||
liste -> "[...]";
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
../../background.jpg
|
|
@ -18,7 +18,7 @@ for file_to_test in args.file:
|
|||
source = f.read()
|
||||
|
||||
with NamedTemporaryFile(mode="w", suffix=".py") as f:
|
||||
for example_match in re.finditer("```python.*?```", source, re.S):
|
||||
for example_match in re.finditer("```py(th|c)on.*?```", source, re.S):
|
||||
example = example_match.group()
|
||||
if "mydata.db" in example:
|
||||
continue
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue