@ asyncio.coroutine vs async def

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 :

  1. Los objetos coroutine nativos no implementan los métodos __iter__ y __next__ . Por lo tanto, no se pueden iterar ni pasar a iter() , list() , tuple() y otros elementos integrados. Tampoco se pueden utilizar en un bucle for..in .

    Un bash de usar __iter__ o __next__ en un objeto coroutine nativo resultará en un TypeError.

  2. Los generadores simples no pueden yield from las coroutinas nativas : al hacerlo se producirá un TypeError.

  3. las coroutinas basadas en generadores (para el código asyncio se deben decorar con @asyncio.coroutine ) se pueden yield from objetos coroutine nativos .

  4. inspect.isgenerator() e inspect.isgeneratorfunction() devuelve False 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.