@asyncio.coroutine vs async def [英] @asyncio.coroutine vs async def
问题描述
使用我见过的 asyncio
库
@asyncio.coroutine定义函数():...
和
异步定义函数():...
可互换使用.
两者在功能上有区别吗?
是的,使用 async def
语法的原生协程和使用 asyncio.coroutine 的基于生成器的协程之间存在功能差异
装饰器.
根据 PEP 492,它引入了async def
语法:
Native coroutine 对象没有实现
__iter__
并且__next__
方法.因此,它们不能被迭代或传递到iter()
、list()
、tuple()
和其他内置函数.他们也不能在for..in
循环中使用.尝试在原生协程上使用
__iter__
或__next__
对象将导致 TypeError .普通生成器不能
yield from
native coroutines:这样做将导致 TypeError .基于生成器的协程(对于 asyncio 代码必须用
@asyncio.coroutine
) 可以yield from
原生协程对象.inspect.isgenerator()
和inspect.isgeneratorfunction()
为原生协程返回False
对象和原生协程函数.
上面的第 1 点意味着,虽然使用 @asyncio.coroutine
装饰器语法定义的协程函数可以像传统的生成器函数一样运行,但使用 async def
语法定义的协程函数不能.
这里有两个最小的、表面上等效的协程函数,用两种语法定义:
导入异步@asyncio.coroutine定义装饰(x):来自 x 的收益异步定义本机(x):等待 x
虽然这两个函数的字节码几乎相同:
<预><代码>>>>导入文件>>>dis.dis(装饰)5 0 LOAD_FAST 0 (x)3 GET_YIELD_FROM_ITER4 LOAD_CONST 0(无)7 YIELD_FROM8 POP_TOP9 LOAD_CONST 0(无)12 RETURN_VALUE>>>dis.dis(本地)8 0 LOAD_FAST 0 (x)3 GET_AWAITABLE4 LOAD_CONST 0(无)7 YIELD_FROM8 POP_TOP9 LOAD_CONST 0(无)12 RETURN_VALUE...唯一的区别是 GET_YIELD_FROM_ITER
与 GET_AWAITABLE
,当尝试迭代它们返回的对象时,它们的行为完全不同:
显然 'foo'
不是可等待的,所以尝试用它调用 native()
没有多大意义,但希望这一点很清楚它返回的 coroutine
对象是不可迭代的,无论其参数如何.
Brett Cannon 对 async
/await
语法的更详细调查:在 Python 3.5 中 async/await 到底是如何工作的? 更深入地介绍了这种差异.
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?
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.
这篇关于@asyncio.coroutine vs async def的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!