【问题标题】:How is this a coroutine?这怎么是协程?
【发布时间】:2017-02-09 01:54:15
【问题描述】:

我正在尝试了解 Python(以及一般情况下)中的协程。一直在阅读有关理论、概念和一些示例的信息,但我仍在苦苦挣扎。我了解异步模型(做了一点 Twisted),但还不了解协程。

一个tutorial 给出了这个作为协程示例(我做了一些更改来说明我的问题):

async def download_coroutine(url, number):
    """
    A coroutine to download the specified url
    """
    request = urllib.request.urlopen(url)
    filename = os.path.basename(url)
    print("Downloading %s" % url)

    with open(filename, 'wb') as file_handle:
        while True:
            print(number) # prints numbers to view progress
            chunk = request.read(1024)
            if not chunk:
                print("Finished")
                break
            file_handle.write(chunk)
    msg = 'Finished downloading {filename}'.format(filename=filename)
    return msg

这是用这个运行的

coroutines = [download_coroutine(url, number) for number, url in enumerate(urls)]
completed, pending = await asyncio.wait(coroutines)

查看生成器协程示例,我可以看到一些 yield 语句。这里什么都没有,而且 urllib 是同步的,AFAIK。

另外,由于代码应该是异步的,我希望看到一系列交错的数字。 (1, 4, 5, 1, 2, ..., "完成", ...) 。我看到的是一个重复以Finished 结尾的数字,然后是另一个数字(3、3、3、3、...“已完成”、1、1、1、1、...、“已完成” " ...)。

在这一点上我很想说教程是错误的,这是一个协程只是因为它前面有异步。

【问题讨论】:

  • 你的是协程只是,因为你使用了async def。它不是一个非常合作的,因为它从不屈服于其他协同程序。所以是的,你的分析是正确的。
  • 我最初编写该教程时犯了一个错误。已更新为使用aiohttp

标签: python python-3.x async-await coroutine


【解决方案1】:

coroutine中的co代表cooperative。让步(对其他例程)实际上使例程成为协同例程,因为只有在等待时让步,其他协同例程才能交错。在 Python 3.5 及更高版本的新 async 世界中,这通常是通过 await-ing 来自其他协程的结果来实现的。

根据该定义,您找到的代码不是协程。就Python而言,它是一个协程object,因为这是赋予使用async def创建的函数对象的类型。

所以是的,这个教程是……没有帮助的,因为他们在协程函数中使用了完全同步、不合作的代码。

需要一个异步 HTTP 库而不是 urllib。赞aiohttp

import aiohttp

async def download_coroutine(url):
    """
    A coroutine to download the specified url
    """
    filename = os.path.basename(url)
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            with open(filename, 'wb') as fd:
                while True:
                    chunk = await resp.content.read(1024)
                    if not chunk:
                        break
                    fd.write(chunk)
    msg = 'Finished downloading {filename}'.format(filename=filename)
    return msg

在等待建立连接、等待更多网络数据以及再次关闭会话时,此协程可以让步给其他例程。

我们可以进一步使文件写入异步,但has portability issues; aiofiles project 库使用线程来卸载阻塞调用。使用该库,代码需要更新为:

import aiofiles

async with aiofiles.open(filename, 'wb') as fd:
    while True:
        chunk = await resp.content.read(1024)
        if not chunk:
            break
        await fd.write(chunk)

注意:博客文章已更新以解决这些问题。

【讨论】:

  • 我是一名长期的python2.7程序员,出于某种原因,我一直在避免使用较新的python。我发现原始教程和这些 SO 条目非常有帮助。我很困惑这个问题/答案没有更多的赞成票。学科领域是否专业?干得好!
  • @Greg:协程是 Python 中一个相对较新的补充,所以还没有很多人使用它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-10
  • 2011-01-27
  • 2020-05-25
相关资源
最近更新 更多