from yield to await——Python協程演進過程(一)

這個系列是講python中協程模塊的演進的,也算是《Fluent python》相關章節的一個讀後感。其實對於3.5+的用戶來說,可以直接用await。不過,了解一下演進過程有助於對協程的理解。

Milestone

協程相關的關鍵字和方法的引入:

Python 2.2(2001年)yield

Python 2.5(2006年) .send() .throw() .close()

Python 3.3(2012年) yield from

Python 3.5(2015年)async await

Duck type

在面向對象的支持上,C++採用了多重繼承等一系列C++風格的東西,Java是單繼承+介面,JavaScript之前是Prototype,而Python採用了多繼承+duck type,並用super來安全的初始化父類。而同樣喜歡宣傳自己優雅的設計的Ruby也是採用的Duck Type。

什麼是Duck Type

當我看到一隻鳥走路像鴨子,游泳像鴨子,叫聲像鴨子,那我就把它叫做鴨子。(When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.)n

簡而言之就是,判斷一個對象的類型不是看他爸爸是誰,而是他實現了什麼方法。

舉個例子:

>>> class Foo:n... def __iter__(self):n... passn...n>>> from collections import Iterablen>>> isinstance(Foo(),Iterable)nTruen

在命令行里運行如上代碼,會發現Foo類並沒有繼承任何迭代相關的父類,但卻被認為是可迭代對象。正是由於Duck Type,避免了複雜的繼承關係。

Iterator

解釋器在判斷要給對象是否是可迭代對象時,是這麼做的:n

  1. 判斷是否有__iter__,獲取一個迭代器。n
  2. 實現了__getitem__,創建一個迭代器,按索引獲取元素。n
  3. 不滿足以上條件則對象不可迭代,拋出異常。

可迭代對象的UML圖如下:

通過__iter__方法返回一個迭代器,而迭代器需要實現一個__next__方法和__iter__方法。

Generators

生成器是為了滿足惰性載入而出現的,比如一些數據在使用時才生產出來,而不是初始化時就載入到內存里了。在編程中,英文含有lazy的相關專業辭彙大多都是這樣的作用。

在python中,一個函數中如果有yield關鍵詞,那麼這個函數就是個生成器函數,該函數調用時會立即返回一個生成器對象,而不是函數結果。而要獲取該函數產出的結果,需要調用next方法(根據不同版本,有的是雙下劃線版本的__next__方法)。

協程的定義是,一個可以隨時掛起並讓出CPU控制權的控制流。惰性載入在協程中的意義在於,可以通過將未來的控制流放在next方法中,使得程序暫時讓出CPU,在需要時通過send方法轉進回來。

Summary

Python的Duck Type保證了一種靈活的面向對象實踐。

在迭代器基礎上實現了惰性載入的生成器,之後為了完善生成器,提供了send等方法,和原有的next方法配合,是實現協程的基礎。

from yield to await--Python協程演進過程(一)

from yield to await--Python協程演進過程(二)

from yield to await--Python協程演進過程(三)

python雜七雜八的使用經驗 - 知乎專


推薦閱讀:

想擴展知識,學一門新語言,該學 Python、Ruby,還是 C++ ?
為什麼 sqlmap 源碼看起來那麼費勁?
50行Python代碼實現圖片轉字元畫,今晚直播不見不散
大家都用 Python 來做什麼啊?

TAG:协程 | Python | 并发 |