@asyncio.coroutine 与异步定义

@asyncio.coroutine vs async def

有了我见过的 asyncio 库,

@asyncio.coroutine
def function():
    ...

async def function():
    ...

可互换使用。

两者在功能上有什么区别吗?

async def 是 Python 3.5 的新语法。 您可以在 async def 内使用 awaitasync withasync for

@coroutineasync def 的功能类似物,但它在 Python 3.4+ 中工作,并利用 yield from 构造而不是 await.

如果您的 Python 是 3.5+,从实用的角度来看,永远不要使用 @coroutine

Python 3.5 coroutines 正式成为一个独特的类型,因此 async def 语法以及 await 语句.

在此之前,Python 3.4 通过将常规函数包装到 generators 中来创建协程,因此装饰器语法和更像生成器的 yield from

是的,使用 async def 语法的原生协程与使用 asyncio.coroutine 装饰器的基于生成器的协程之间存在功能差异。

根据 PEP 492,它引入了 async def 语法:

  1. Native coroutine objects do not implement __iter__ and __next__ methods. Therefore, they cannot be iterated over or passed to iter(), list(), tuple() and other built-ins. They also cannot be used in a for..in loop.

    An attempt to use __iter__ or __next__ on a native coroutine object will result in a TypeError .

  2. Plain generators cannot yield from native coroutines: doing so will result in a TypeError .

  3. generator-based coroutines (for asyncio code must be decorated with @asyncio.coroutine) can yield from native coroutine objects.

  4. inspect.isgenerator() and inspect.isgeneratorfunction() return False 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_ITERGET_AWAITABLE,当尝试遍历它们 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

显然 'foo' 不是可等待的,因此尝试用它调用 native() 没有多大意义,但希望很清楚 coroutine 反对它returns 不可迭代,无论其参数如何。

Brett Cannon 对 async/await 语法的更详细研究:How the heck does async/await work in Python 3.5? 更深入地介绍了这种差异。