@asyncio.coroutine 与 async def
Posted
技术标签:
【中文标题】@asyncio.coroutine 与 async def【英文标题】:@asyncio.coroutine vs async def 【发布时间】:2017-03-27 02:08:36 【问题描述】:使用我见过的asyncio
库,
@asyncio.coroutine
def function():
...
和
async def function():
...
可以互换使用。
两者在功能上有区别吗?
【问题讨论】:
【参考方案1】:async def
是 Python 3.5 的新语法。
您可以在async def
s 中使用await
、async with
和async for
。
@coroutine
是 async def
的功能类似物,但它适用于 Python 3.4+ 并使用 yield from
构造而不是 await
。
如果您的 Python 版本为 3.5+,请不要使用 @coroutine
。
【讨论】:
很高兴知道为什么在 3.5+ 中“从不使用@coroutine”。有真正的原因还是意见/经验法则?例如,我在 3.4 继承的代码库中有一些 @coroutine 装饰器,但所有新开发都在 3.5 中。我应该把那些装饰器变成async def
吗?
要暂停协程执行,需要yield
。但是本机协程内部不允许使用yield
!所以看起来即使在 Python 3.5+ 中仍然需要使用@coroutine
没有。只需使用await asyncio.sleep(0)
。
@hmijail @coroutine
标记的生成器可以等待,但如果可能的话最好将它们转换为async def
。
await asyncio.sleep(10)
效果很好。此外,我在 asyncio master 中将 asyncio.sleep()
转换为 async def
(将作为 Python 3.7 的一部分发布)。【参考方案2】:
从 Python 3.5 coroutines
正式成为一个独特的类型,因此 async def
语法以及 await
语句。
在此之前,Python 3.4 通过将常规函数包装到 generators
中来创建协程,因此使用了装饰器语法,并且更类似于生成器 yield from
。
【讨论】:
【参考方案3】:是的,使用 async def
语法的原生协程和使用 asyncio.coroutine
装饰器的基于生成器的协程之间存在功能差异。
根据PEP 492,其中引入了async def
语法:
本机协程对象不实现
__iter__
和__next__
方法。因此,它们不能被迭代或传递 到iter()
、list()
、tuple()
和其他内置插件。他们也 不能在for..in
循环中使用。尝试在原生协程上使用
__iter__
或__next__
对象将导致 TypeError 。普通生成器不能
yield from
原生协程:这样做 将导致 TypeError 。基于生成器的协程(对于 asyncio 代码必须用
@asyncio.coroutine
) 可以yield from
原生协程对象。
inspect.isgenerator()
和inspect.isgeneratorfunction()
为 本机协程 对象和 本机协程函数返回False
。
上面的第 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? 更深入地涵盖了这种差异。
【讨论】:
谢谢!但是为什么inspect.iscoroutinefunction()
返回 False
用于原生协程函数呢?
@roachsinai 它doesn't.以上是关于@asyncio.coroutine 与 async def的主要内容,如果未能解决你的问题,请参考以下文章