使用我见过的 asyncio
库,
@asyncio.coroutine
def function():
...
和
async def function():
...
可以互换使用。
两者在功能上有区别吗?
最佳答案
是的,使用 async def
语法的原生协程和使用 asyncio.coroutine
装饰器的基于生成器的协程之间存在功能差异。
根据PEP 492 ,其中引入了 async def
语法:
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.
上面的第 1 点意味着虽然使用 @asyncio.coroutine
装饰器语法定义的协程函数可以像传统的生成器函数一样运行,但使用 async def
语法定义的协程函数则不能。
这里有两个最小的,表面上等效的协程函数,用两种语法定义:
import asyncio
@asyncio.coroutine
def decorated(x):
yield from x
async def native(x):
await x
虽然这两个函数的字节码几乎相同:
>>> 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
...唯一的区别是 GET_YIELD_FROM_ITER
与 GET_AWAITABLE
,当尝试迭代它们返回的对象时,它们的行为完全不同:
>>> 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
显然 'foo'
不是可等待的,因此尝试用它调用 native()
没有多大意义,但希望这一点很清楚它返回的 coroutine
对象是不可迭代的,不管它的参数是什么。
Brett Cannon 对 async
/await
语法的更详细调查:How the heck does async/await work in Python 3.5?更深入地涵盖了这种差异。
关于python - @asyncio.coroutine 与 async def,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40571786/