With the asyncio
library I've seen,
@asyncio.coroutine def function(): ...
and
async def function(): ...
used interchangeably.
Is there any functional difference between the two?
async def is a new syntax from Python 3.5. You could use await , async with and async for inside async def s. @coroutine is a functional analogue for async def but it works in Python 3.4+ and utilizes yield from construction instead of await . For practical perspective just never use @coroutine if your Python is 3.5+.
coroutine was also deprecated in python 3.7 and scheduled for removal in python 3.10. It already issues a deprecation warning if used.
asyncio is a library to write concurrent code using the async/await syntax. asyncio is used as a foundation for multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.
The syntax async def introduces either a native coroutine or an asynchronous generator. The expressions async with and async for are also valid, and you'll see them later on. The keyword await passes function control back to the event loop. (It suspends the execution of the surrounding coroutine.)
Yes, there are functional differences between native coroutines using async def
syntax and generator-based coroutines using the asyncio.coroutine
decorator.
According to PEP 492, which introduces the async def
syntax:
Native coroutine objects do not implement
__iter__
and__next__
methods. Therefore, they cannot be iterated over or passed toiter()
,list()
,tuple()
and other built-ins. They also cannot be used in afor..in
loop.An attempt to use
__iter__
or__next__
on a native coroutine object will result in a TypeError .Plain generators cannot
yield from
native coroutines: doing so will result in a TypeError .generator-based coroutines (for asyncio code must be decorated with
@asyncio.coroutine
) canyield from
native coroutine objects.
inspect.isgenerator()
andinspect.isgeneratorfunction()
returnFalse
for native coroutine objects and native coroutine functions.
Point 1 above means that while coroutine functions defined using the @asyncio.coroutine
decorator syntax can behave as traditional generator functions, those defined with the async def
syntax cannot.
Here are two minimal, ostensibly equivalent coroutine functions defined with the two syntaxes:
import asyncio @asyncio.coroutine def decorated(x): yield from x async def native(x): await x
Although the bytecode for these two functions is almost identical:
>>> 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
... the only difference being GET_YIELD_FROM_ITER
vs GET_AWAITABLE
, they behave completely differently when an attempt is made to iterate over the objects they return:
>>> 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
Obviously 'foo'
is not an awaitable, so the attempt to call native()
with it doesn't make much sense, but the point is hopefully clear that the coroutine
object it returns is not iterable, regardless of its argument.
A more detailed investigation of the async
/await
syntax by Brett Cannon: How the heck does async/await work in Python 3.5? covers this difference in more depth.
async def
is a new syntax from Python 3.5. You could use await
, async with
and async for
inside async def
s.
@coroutine
is a functional analogue for async def
but it works in Python 3.4+ and utilizes yield from
construction instead of await
.
For practical perspective just never use @coroutine
if your Python is 3.5+.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With