Avec la bibliothèque asyncio
que j'ai vue,
@asyncio.coroutine
def function():
...
et
async def function():
...
utilisé de manière interchangeable.
Y a-t-il une différence fonctionnelle entre les deux?
Oui, il existe des différences fonctionnelles entre les coroutines natives utilisant la syntaxe async def
Et les coroutines basées sur le générateur utilisant le décorateur asyncio.coroutine
.
Selon PEP 492 , qui introduit la syntaxe async def
:
Les objets coroutine natifs n'implémentent pas les méthodes
__iter__
Et__next__
. Par conséquent, ils ne peuvent pas être itérés ou passés àiter()
,list()
,Tuple()
et à d'autres éléments intégrés. Ils ne peuvent pas non plus être utilisés dans une bouclefor..in
.Une tentative d'utilisation de
__iter__
Ou__next__
Sur un objet coroutine natif entraînera une erreur TypeError.Les générateurs simples ne peuvent pas
yield from
coroutines natives : le faire entraînera une TypeError.coroutines basées sur générateur (pour le code asyncio doit être décoré avec
@asyncio.coroutine
) peutyield from
objets coroutine natifs .
inspect.isgenerator()
etinspect.isgeneratorfunction()
returnFalse
pour coroutine native objets et fonctions coroutines natives .
Le point 1 ci-dessus signifie que si les fonctions coroutine définies à l'aide de la syntaxe décoratrice @asyncio.coroutine
Peuvent se comporter comme des fonctions génératrices traditionnelles, celles définies avec la syntaxe async def
Ne le peuvent pas.
Voici deux fonctions coroutines minimales, ostensiblement équivalentes, définies avec les deux syntaxes:
import asyncio
@asyncio.coroutine
def decorated(x):
yield from x
async def native(x):
await x
Bien que le bytecode pour ces deux fonctions soit presque identique:
>>> import dis
>>> dis.dis(decorated)
5 0 LOAD_FAST 0 (x)
3 GET_YIELD_FROM_ITER
4 LOAD_CONST 0 (None)
7 YIELD_FROM
8 POP_TOP
9 LOAD_CONST 0 (None)
12 RETURN_VALUE
>>> dis.dis(native)
8 0 LOAD_FAST 0 (x)
3 GET_AWAITABLE
4 LOAD_CONST 0 (None)
7 YIELD_FROM
8 POP_TOP
9 LOAD_CONST 0 (None)
12 RETURN_VALUE
... la seule différence étant GET_YIELD_FROM_ITER
vs GET_AWAITABLE
, ils se comportent complètement différemment lors d'une tentative d'itération sur les objets qu'ils retournent:
>>> list(decorated('foo'))
['f', 'o', 'o']
>>> list(native('foo'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'coroutine' object is not iterable
Évidemment, 'foo'
N'est pas un fichier attendu, donc la tentative d'appeler native()
avec lui n'a pas beaucoup de sens, mais le point est, espérons-le, clair que l'objet coroutine
qu'il renvoie n'est pas itérable, quel que soit son argument.
Une étude plus détaillée de la syntaxe async
/await
par Brett Cannon: Comment diable l'async/attente fonctionne-t-il dans Python 3.5? couvre cette différence plus en profondeur.
async def
est une nouvelle syntaxe de Python 3.5. Vous pouvez utiliser await
, async with
et async for
à l'intérieur async def
s.
@coroutine
est un analogue fonctionnel de async def
mais cela fonctionne en Python 3.4+ et utilise yield from
construction au lieu de await
.
Pour une perspective pratique, n'utilisez jamais @coroutine
si votre Python est 3,5+.
De Python 3.5coroutines
est officiellement devenu un type distinct et donc le async def
syntaxe, ainsi que les instructions await
.
Avant cela, Python 3.4 créait des coroutines en encapsulant des fonctions régulières dans generators
, d'où la syntaxe du décorateur et la plus semblable à un générateur yield from
.