run - python async api
@ asyncio.coroutine vs async def (3)
Con la biblioteca de asyncio
que he visto,
@asyncio.coroutine
def function():
...
y
async def function():
...
Utilizado indistintamente.
¿Hay alguna diferencia funcional entre los dos?
Desde Python 3.5, las coroutines
formalmente se convirtieron en un tipo distinto y, por lo tanto, la sintaxis de async def
, junto con las declaraciones de await
.
Antes de eso, Python 3.4 creaba coroutines envolviendo funciones regulares en generators
, de ahí la sintaxis del decorador, y el mayor yield from
generador.
Sí, hay diferencias funcionales entre las coroutinas nativas que utilizan la sintaxis de async def
y las coroutinas basadas en generadores que usan el decorador asyncio.coroutine
.
Según PEP 492 , que introduce la sintaxis de async def
:
Los objetos nativos de corutina no implementan los métodos
__iter__
y__next__
. Por lo tanto, no se pueden iterar ni pasar aiter()
,list()
,tuple()
y otros elementos integrados. Tampoco pueden usarse en un ciclofor..in
.Un intento de usar
__iter__
o__next__
en un objeto coroutine nativo resultará en un TypeError.Los generadores simples no pueden
yield from
corutinas nativas : al hacerlo, se generará un TypeError.Las corutinas basadas en generadores (para el código asyncio deben estar decoradas con
@asyncio.coroutine
) puedenyield from
objetos corindinos nativos .
inspect.isgenerator()
einspect.isgeneratorfunction()
devuelveFalse
para los objetos nativos de coroutine y las funciones nativas de coroutine .
El punto 1 anterior significa que, aunque las funciones de corutina definidas con la sintaxis del decorador @asyncio.coroutine
pueden comportarse como funciones de generador tradicionales, las definidas con la sintaxis de async def
Asíncrona no.
Aquí hay dos funciones de corutina mínimas, aparentemente equivalentes, definidas con las dos sintaxis:
import asyncio
@asyncio.coroutine
def decorated(x):
yield from x
async def native(x):
await x
Aunque el bytecode para estas dos funciones es casi idéntico:
>>> 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 única diferencia es GET_YIELD_FROM_ITER
frente a GET_AWAITABLE
, se comportan de forma completamente diferente cuando se intenta iterar sobre los objetos que devuelven:
>>> 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
Obviamente, ''foo''
no es algo que se pueda esperar, por lo que el intento de llamar a Native native()
no tiene mucho sentido, pero el punto es esperanzador de que el objeto que devuelve no es iterable, independientemente de su argumento.
Una investigación más detallada de la sintaxis async
/ await
de Brett Cannon: ¿Cómo diablos hace async / await work en Python 3.5? cubre esta diferencia con más profundidad.
async def
es una nueva sintaxis de Python 3.5. Puede usar await
, async with
y async for
interior async def
s.
@coroutine
es un análogo funcional para la async def
pero funciona en Python 3.4+ y utiliza el yield from
construcción en lugar de la await
.
Para una perspectiva práctica, nunca use @coroutine
si su Python es 3.5+.