关于python:@ asyncio.coroutine与异步def

@asyncio.coroutine vs async def

借助我所见过的asyncio库,

1
2
3
@asyncio.coroutine
def function():
    ...

1
2
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
    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 .

  • 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) can yield from native coroutine objects.

  • inspect.isgenerator() and inspect.isgeneratorfunction() return False for native coroutine objects and native coroutine functions.

  • 上面的要点1表示,虽然使用@asyncio.coroutine装饰器语法定义的协程函数可以像传统的生成器函数那样工作,但是使用async def语法定义的协程函数不能。

    这是用两种语法定义的两个最小的,表面上等效的协程函数:

    1
    2
    3
    4
    5
    6
    7
    8
    import asyncio

    @asyncio.coroutine
    def decorated(x):
        yield from x

    async def native(x):
        await x

    尽管这两个函数的字节码几乎相同:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    >>> 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,当尝试遍历返回的对象时,它们的行为完全不同:

    1
    2
    >>> list(decorated('foo'))
    ['f', 'o', 'o']

    1
    2
    3
    4
    >>> 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语法进行了更详细的研究:异步/等待在Python 3.5中如何工作?更深入地涵盖了这种差异。


    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中来创建协程。