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?
Sí, hay diferencias funcionales entre las coroutinas nativas que utilizan la syntax de async def
y las coroutinas basadas en generadores que usan el decorador asyncio.coroutine
.
Según PEP 492 , que introduce la syntax de async def
:
Los objetos coroutine nativos 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 se pueden utilizar en un buclefor..in
.Un bash de usar
__iter__
o__next__
en un objeto coroutine nativo resultará en un TypeError.Los generadores simples no pueden
yield from
las coroutinas nativas : al hacerlo se producirá un TypeError.las coroutinas basadas en generadores (para el código asyncio se deben decorar con
@asyncio.coroutine
) se puedenyield from
objetos coroutine nativos .
inspect.isgenerator()
einspect.isgeneratorfunction()
devuelveFalse
para los objetos nativos de coroutine y las funciones nativas de coroutine .
El punto 1 anterior significa que, si bien las funciones básicas definidas con la syntax del decorador @asyncio.coroutine
pueden comportarse como funciones de generador tradicionales, las definidas con la syntax de async def
no pueden.
Aquí hay dos funciones de correlación mínimas, ostensiblemente equivalentes, definidas con las dos syntax:
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 que GET_YIELD_FROM_ITER
vs GET_AWAITABLE
, se comportan de manera completamente diferente cuando se hace un bash de iterar sobre los objetos que devuelven:
>>> list(decorated('foo')) ['f', 'o', 'o']
>>> list(native('foo')) Traceback (most recent call last): File "", line 1, in TypeError: 'coroutine' object is not iterable
Obviamente, 'foo'
no es algo que se pueda esperar, por lo que el bash 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 syntax de async
/ await
por Brett Cannon: ¿Cómo diablos funciona async / await en Python 3.5? Cubre esta diferencia con mayor profundidad.
async def
es una nueva syntax de Python 3.5. Puede usar await
, async with
y async for
dentro de 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+.
Desde Python 3.5, las coroutines
formalmente se convirtieron en un tipo distinto y, por lo tanto, la syntax de async def
, junto con las declaraciones de await
.
Antes de eso, Python 3.4 creaba coroutines envolviendo las funciones regulares en generators
, de ahí la syntax del decorador, y el yield from
más parecido a un generador.