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 fromcorutinas nativas : al hacerlo, se generará un TypeError.Las corutinas basadas en generadores (para el código asyncio deben estar decoradas con
@asyncio.coroutine) puedenyield fromobjetos corindinos nativos .
inspect.isgenerator()einspect.isgeneratorfunction()devuelveFalsepara 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+.