【发布时间】:2017-03-27 02:08:36
【问题描述】:
使用我见过的asyncio 库,
@asyncio.coroutine
def function():
...
和
async def function():
...
可以互换使用。
两者在功能上有区别吗?
【问题讨论】:
标签: python python-3.x async-await python-3.5 python-asyncio
使用我见过的asyncio 库,
@asyncio.coroutine
def function():
...
和
async def function():
...
可以互换使用。
两者在功能上有区别吗?
【问题讨论】:
标签: python python-3.x async-await python-3.5 python-asyncio
async def 是 Python 3.5 的新语法。
您可以在async defs 中使用await、async with 和async for。
@coroutine 是 async def 的功能类似物,但它适用于 Python 3.4+ 并使用 yield from 构造而不是 await。
如果您的 Python 版本为 3.5+,请不要使用 @coroutine。
【讨论】:
async def吗?
yield。但是本机协程内部不允许使用yield!所以看起来即使在 Python 3.5+ 中仍然需要使用@coroutine
await asyncio.sleep(0)。
@coroutine 标记的生成器可以等待,但如果可能的话最好将它们转换为async def。
await asyncio.sleep(10) 效果很好。此外,我在 asyncio master 中将 asyncio.sleep() 转换为 async def(将作为 Python 3.7 的一部分发布)。
从 Python 3.5 coroutines 正式成为一个独特的类型,因此 async def 语法以及 await 语句。
在此之前,Python 3.4 通过将常规函数包装到 generators 中来创建协程,因此使用了装饰器语法,并且更类似于生成器 yield from。
【讨论】:
是的,使用 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 用于原生协程函数呢?