Import: Paris.py #14.
This commit is contained in:
parent
af2344a0d3
commit
7b95b3504c
|
@ -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` jusqu’au `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
|
Loading…
Reference in New Issue