来自asyncio.sleep的yield会起作用,但async def的yield会失败

时间:2017-01-17 01:57:25

标签: python python-3.x python-asyncio python-3.6

以下代码可以正常使用:

import asyncio
loop = asyncio.get_event_loop()
async def a ():
    print('hello')
def b ():
    yield from asyncio.sleep(1)
loop.run_until_complete(b())
loop.close()
print('done')

但是,以下失败:

import asyncio
loop = asyncio.get_event_loop()
async def a ():
    print('hello')
def b ():
    yield from a() # <=========== only 1 tiny change
loop.run_until_complete(b())
loop.close()
print('done')

使用b装饰@asyncio.coroutine使其有效。

但是,问题是为什么第一段代码在没有@asyncio.coroutine装饰器的情况下工作正常?文档清楚地说asyncio.sleep是一个协程,a也是如此,那么为什么代码在一个案例中失败并且在另一个案例中工作正常呢?

1 个答案:

答案 0 :(得分:4)

您的代码会产生以下错误:

...
        yield from a()  # <=========== only 1 tiny change
TypeError: cannot 'yield from' a coroutine object in a non-coroutine generator

正如错误消息中明确指出的那样,使用asyncio时,您应该使用@coroutineasync def来标记协同程序。在async def中,应使用await代替yield from

import asyncio


async def a():
    print('hello')


async def b():
    await a()


loop = asyncio.get_event_loop()
loop.run_until_complete(b())
loop.close()
print('done')

或者,对于python 3.4:

import asyncio

@asyncio.coroutine
def a():
    print('hello')


@asyncio.coroutine
def b():
    yield from a()


loop = asyncio.get_event_loop()
loop.run_until_complete(b())
loop.close()

print('done')

你的第一个例子被认为是&#34; buggy&#34;,但它正在执行&#34;正确&#34;因为run_until_complete调用iscoroutine当前为生成器返回True(任何带有def / yield的{​​{1}},但这是一个可能会更改的实现细节未来的python版本。在yield from上使用@couroutine(而不是def a()),或者只是将async def a()添加到常规yield from asyncio.sleep(1),您的第二个示例也会运行。目前python可能是&#34; merciful&#34;当使用未在asyncio中标记为协程的生成器时,但在使用def a()时则不使用。