2016-09-30 45 views
4

我想了解Python中的协程(以及一般)。一直在阅读理论,概念和几个例子,但我仍在挣扎。我理解异步模型(做了一点扭曲),但没有协程。这是一个协程?

一个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,...,“完成”。 ..)。

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

+2

你是一个协同例程*,因为你使用了'async def'。它不是一个非常合作的,因为它从来没有屈服于其他的共同惯例。所以是的,你的分析是正确的。 –

+0

当我最初编写该教程时,我犯了一个错误。它已更新为使用'aiohttp' –

回答

12

协程代表合作合作。屈服(对其他例程而言)使得例程成为一个协同例程,实际上,因为只有在等待时可以屈服,其他协同例程才能交错。在Python 3.5及更高版本的新的async世界中,通常可以通过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 

等待要建立连接时,并等待更多的网络数据时,也再次在会议闭幕时,作为该协程可以提供给其他程序。

我们可以进一步使文件写入异步,但是那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) 

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

+0

我是一个很长时间的Python2.7程序员,出于某种原因,我一直在避免较新的Python。我发现原始教程和这些SO条目非常有帮助。我很困惑这个问题/答案没有更多upvotes。专业领域是专业吗?干得不错! – Greg

+0

@Greg:协程是Python的一个相对较新的增加,所以还没有那么多与它们一起工作。 –