tutorial run espaƱol await asyncio async python python-3.x async-await python-3.5 python-asyncio

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 :

  1. Los objetos nativos de corutina 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 pueden usarse en un ciclo for..in .

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

  2. Los generadores simples no pueden yield from corutinas nativas : al hacerlo, se generará un TypeError.

  3. Las corutinas basadas en generadores (para el código asyncio deben estar decoradas con @asyncio.coroutine ) pueden yield from objetos corindinos 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, 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+.