这是一个协程怎么样?

Dum*_*tru 5 python coroutine python-3.x async-await

我试图理解Python中的协同程序(一般而言).一直在阅读关于理论,概念和一些例子,但我仍然在苦苦挣扎.我理解异步模型(做了一些Twisted)但不是协同程序.

一个教程将此作为一个协程示例(我做了一些更改来说明我的问题):

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
Run Code Online (Sandbox Code Playgroud)

这与此一起运行

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

看看生成器协同程序示例,我可以看到一些yield语句.这里什么也没有,urllib是同步的,AFAIK.

此外,由于代码应该是异步的,我期待看到一系列交错的数字.(1,4,5,1,2,......,"完成",......).我所看到的是一个重复结尾的单个数字Finished,然后是另一个(3,3,3,3,......"完成",1,1,1,1,......,"完成"... ).

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

Mar*_*ers 13

合作协程代表合作.屈服(对其他例程)使例程成为一个共同例程,实际上,因为只有在等待时屈服其他协同例程才能交错.在asyncPython 3.5及更高版本的新世界中,通常是通过await其他协同程序的结果来实现的.

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

所以,是的,教程是无益的,因为他们在协程函数中使用了完全同步的,不合作的代码.

而不是urllib,需要一个异步HTTP库.喜欢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
Run Code Online (Sandbox Code Playgroud)

在等待建立连接时,以及等待更多网络数据时,以及再次关闭会话时,此协程可以产生其他例程.

我们可以进一步使文件写入异步,但这有可移植性问题 ; 该aiofiles项目库使用线程来卸载阻塞调用.使用该库,代码需要更新为:

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)
Run Code Online (Sandbox Code Playgroud)

注意:此博客帖子已更新以解决这些问题.