2016-09-30 43 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的一個相對較新的增加,所以還沒有那麼多與它們一起工作。 –