根据异步def

时间:2017-11-18 17:31:13

标签: python async-await python-asyncio coroutine

以下示例定义了一个协程multiply,它等待一个值,将其乘以常数因子,然后打印结果。另一个函数product_table使用multiply生成产品表。

def multiply(factor):
    print(f"product table for {factor}")
    while True:
        sent_value = yield
        result = factor * sent_value
        print(f"{factor} x {sent_value} = {result}")

def product_table(coro):
    coro.send(None) # start coroutine
    for value in range(1, 11):
        coro.send(value)

product_table(multiply(3))

运行示例会产生:

product table for 3
3 x 1 = 3
3 x 2 = 6
3 x 3 = 9
3 x 4 = 12
3 x 5 = 15
3 x 6 = 18
3 x 7 = 21
3 x 8 = 24
3 x 9 = 27
3 x 10 = 30

我试图在async defawait方面实现完全相同的示例,但我无处可去。我原来的,不正确的期望是使用await的以下协程函数等同于依赖于yield的协程:

async def multiply(factor):
    print(f"product table for {factor}")
    while True:
        await sent_value
        result = factor * sent_value
        print(f"{factor} x {sent_value} = {result}")

我预计这会起作用听起来很愚蠢,但对于我沉闷的大脑,它会读到“等待发送的价值”。事实并非如此 - 我得到NameError: name 'sent_value' is not defined

到目前为止,我的理解有限,您可以根据yield(如第一个工作示例中所示),来定义协程函数async defawait一样,正如我在第二个例子中所尝试的那样。情况似乎并非如此。

我的具体问题是:

  1. 您如何根据async def
  2. 实施第一个示例
  3. 根据async def
  4. 实施是否有意义?

    我讨厌我发现的所有奇怪的例子都是在虚假管道中使用time.sleep(0.1)到处都是。我正在尝试撰写一篇博客文章,其中包含使用更具体(如果也是微不足道的)管道的示例。

    [删除了错误的编辑]

3 个答案:

答案 0 :(得分:3)

  

在async def方面实现是否有意义?

不,我认为不是。

碰巧asyncio模块是使用生成器实现的,但理论上,它可以在没有生成器的情况下实现。没有必要尝试将asyncio用于生成器特定的工作。想象一下asyncio协同程序和生成器协同程序 - 是不同的东西。

粗略地说,只有在需要并行执行某些I / O操作(例如下载多个URL)时才应使用asyncioMore about it

答案 1 :(得分:2)

await yield from 相同,而不是yield;它将生成器的控制委托给下一个

您没有委派控制权,因此您不会使用async defawait;您只是将数据发送到另一台发电机。如果发送了异常(使用generator.throw()),则不会将其传递给您的multiply()生成器。

async def的目的是创建等待协程,await仅适用于等待。目标是您可以将这些链接在一起并仍然有效地将信息返回到驱动它们的事件循环。 yield未在等待的协程中使用(甚至是合法的)。

您可能希望阅读Python核心开发人员Brett Cannon撰写的关于async defawait以及yield from@asyncio.coroutine联系在一起的优秀博客文章:{ {3}}

可以将生成器与协程组合在一起;从Python 3.6开始(实现了How the heck does async/await work in Python 3.5?

async def multiply(factor):
    print(f"product table for {factor}")
    while True:
        sent_value = yield
        result = factor * sent_value
        print(f"{factor} x {sent_value} = {result}")

请注意,我没有在此处替换yield。你可以这样使用它:

async def product_table(coro):
    await coro.send(None) # start coroutine
    for value in range(1, 11):
        await coro.send(value)

使这个协程变得没什么意义,你没有在product_table()发生器中使用任何等待物。但是说你想把这些数据发送到网络套接字:

async def multiply(factor):
    await socket.send(f"product table for {factor}")
    while True:
        sent_value = yield
        result = factor * sent_value
        await socket.send(f"{factor} x {sent_value} = {result}")

现在你有了一个事件循环可以为你管理的东西,并行运行多个这样的任务,当多个客户准备好接收数据时,将乘法表发送给它们。

答案 2 :(得分:2)

使用async def时,您正在创建一种新类型的第一类对象,一个协程对象。这些必须由异步事件循环驱动。 await需要调用协程,可以暂停协同程序。这些通常用于IO。

David Beazley为此编写了一个很好的框架,名为curio。您可以看到基本的example echo server