Import: Paris.py #14.

This commit is contained in:
Julien Palard 2023-02-10 16:12:57 +01:00
parent af2344a0d3
commit 7b95b3504c
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
1 changed files with 281 additions and 0 deletions

281
2018-paris.py-14-asyncio.md Normal file
View File

@ -0,0 +1,281 @@
# asyncio (ou pas)
## Julien Palard
- Software Engineer
- cPython core dev
- Coordinator of docs.python.org/fr/
- Python trainer
- hackinscience.org
# Coroutine
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
Pour être exhaustif, il existe deux types de coroutines en Python :
- 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
Qu'on peut manipuler :
```
>>> 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:
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).
## Coroutines
Une coroutine renvoie juste un objet coroutine, donc à un moment, du code devra appeler la méthode `send` dessus.
C'est le rôle d'une *main loop*.
## Récupérer un résultat
Le résultat d'une coroutine est stocké dans l'exception `StopIteration`.
Notes:
Dans l'attribut `value`.
## await
Il existe un mot clé dédié à la récupération de résultat d'une coroutine :
`await`
## await
Lorsqu'on lui donne un *awaitable*, `await` essaye d'obtenir son résultat.
Si l'*awaitable* se suspend, `await` suspends à son tour la coroutine actuelle, récursivemet, jusqu'à l'appel à `send`.
## await
```
async def two():
return 2
async def four():
return await two() + await two()
coro = four()
coro.send(None)
```
Ce qui donne `StopIteration: 4`, de manière complètement synchrone, malgré le vocabulaire utilisé.
## Suspendre une coroutine.
Ce n'est pas possible dans une coroutine.
Mais toute la chaîne d'*await* peut-être suspendue en utilisant un
`yield` depuis un *future-like object*.
## 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` jusquau `send`.
## Awaitables
Les [awaitables](https://www.python.org/dev/peps/pep-0492/#await-expression)
sont des objets pouvant être « attendus » via un `await`.
Donc c'est soit une `coroutine` soit un objet implémentant `__await__`.
# Gérer ses coroutines
Appelons simplement `send`, et attendons-nous à la fameuse `StopIteration` :
```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
Mais si la coroutine se suspend, notre `coro_manager` ne suffira pas :
```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
Donc, un gestionnaire de coroutines doit rappeler send, comme celui-ci :
```python
def frenetic_coro_manager(coro):
try:
while True:
coro.send(None)
except StopIteration as stop:
return stop.value
```
## Gérer ses coroutines
Mais notre manager frénétique ne sait s'occuper que d'une seule
routine, peut-on faire mieux ?
## Gérer ses coroutines
On pourrait relancer le travail de l'une d'entre elles au hasard :
```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
Essayons :
```python
async def tum():
while True:
await Awaitable()
print("Tum")
async def pak():
while True:
await Awaitable()
print("Pak")
frenetic_coros_manager(tum(), pak())
```
Ohh, deux `while True:` qui coopèrent !
# Questions?
- mamot.fr/@mdk
- github.com/JulienPalard
- mdk on #python-fr on libera.chat