Python生成器

 

生成器能夠說是Python和其餘語言較爲不一樣的地方,剛開始看到《Python基礎教程》裏的Python生成器內容時,仍是很不適應,由於之前學過的語言都沒有這種特性,因此理解不能,好在通過反覆閱讀書上的內容和本身編寫代碼測試以後,終於感受理解到點上了,這裏寫篇Blog用來備忘。python

生成器基本概念

從概念上來講,生成器就是用普通方法定義的迭代器,或者更直觀點的講法就是任何包含yield語句的函數都是一個生成器。看一個簡單生成器的代碼1:web

python def repeater(): for i in range(10): yield 1 print list(repeater()) 數據結構

輸出以下:ide

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

這個生成器生成了一組長度爲10,每一個元素都爲1的數據,咱們最後將其轉換成列表形式進行輸出。函數

在這個例子中,若是用print來代替yield咱們也能獲得『類似』的輸出。可是yield的一個顯著特徵就是它不是返回任何值,而是產生值,代碼1中,就是每次循環產生一個1。而當函數執行完以後,並不返回值,也不返回None(這一點很重要,若是隻是簡單地把yield當成print的一種變形就很容易犯這個錯誤),而是返回一個生成器,若是咱們直接輸出代碼1中的repeater()函數:測試

python print repeater() 大數據

將會獲得:spa

<generator object repeater at 0x101a4e410>

就像上面所說的,生成器並不直接返回值,而是返回一個根據你的函數定義的生成器,當用相似list(generator)方法調用的時候,就能夠根據相應的數據結構生成數據。code

再來看一個較爲複雜的生成器代碼2:對象

``` python def flatten(nested): try: for sublist in nested: for element in flatten(sublist): yield element except TypeError: yield nested

print list(flatten([[0,1,2],3,4,[[5,6]],7])) ```

代碼2的輸出爲:

[0, 1, 2, 3, 4, 5, 6, 7]

代碼2的做用就是將一棵樹進行展開,採用了遞歸的思想,它的工做原理就是,若是當前的對象仍是能夠展開的,那就繼續展開,若是沒法展開(Python中int是沒法展開的),就會拋出TypeError這個異常,並且這個引起TypeError的值就是咱們但願獲得的值,因此捕獲這個異常後生成值。

代碼2一樣來自《Python基礎教程》,若是要深究起運行過程,則先須要理解生成器是可迭代的,是能夠爲做爲for循環的展開對象的。而且,yield每次產生一個值,都會將函數凍結,下次則再從該凍結的點繼續執行。拿第一個元素0舉例來講明代碼2的運行過程(如下爲我根據測試程序結果推斷的):

  1. 第一層,運行flatten([[0,1,2],3,4,[[5,6]],7]),展開後sublist爲[0,1,2],進入第二層
  2. 第二層,運行flatten([0,1,2]),展開後sublist爲0,進入第三層
  3. 第三層,運行flatten(0),代碼試圖對0進行展開,Python拋出TypeError,由代碼捕獲到,生成數據0,並返回生成器
  4. 返回第二層,對生成器進行迭代(含數據0),生成數據0,函數凍結,並返回生成器
  5. 返回第一層,對生成器進行迭代(含數據0),生成數據0,函數回到第二層的凍結點繼續執行。

next()方法

next()方法的示例代碼以下:

python def repeater(): while True: yield 1 r = repeater() print r.next()

以上代碼會輸出1,並且若是你繼續調用r.next(),就會繼續輸出1。next()其實就是迭代器,讀取生成器生成的下一個值,這裏能夠看出生成器相較於列表更有優點的一點就是:列表老是先將數據生成並儲存起來,當須要用的時候就去讀取,而生成器都是當須要使用的時候才生成數據,這一點在處理大數據量甚至是無限生成的數據時尤爲有用。

next()方法還須要注意的一點是,對於yield n語句,next()返回的是n的值,而yield n自己的返回值爲None,這一點在send()方法中還會再一次被提到。

send(value)方法

send()方法容許外界向生成器內部傳遞數據,當調用send()方法時,內部將會掛起生成器,yield做爲表達式而非語句使用,yield n將會有一個返回值,該值就是外界經過send()方法傳遞進來的值。send()方法的示例代碼以下:

``` python # When you call next(), n is ever None. When you call send(num), yield is hold up, # so (yield value) return num def repeater(value): while True: n = (yield value)
if n is not None: value = n

r = repeater(11)
print r.next()
r.send(10)
print r.next()

```

輸出爲:

11
10

能夠看到在生成器初始化完後,調用next()獲得的值是咱們初始化時傳遞進去的11,而當調用send()方法向生成器傳了一個值10以後,在調用next()方法,獲得就是後傳進去的值10了。

send()方法須要注意的有兩點:

  1. 經過send()方法傳入生成器的值,由n = (yield value)的形式被生成器內部接收,n即爲傳進來的參數的值。而若是未調用send()方法,n的值爲None。
  2. send()方法不能在生成器剛初始化完成的時候調用,也就說必需要等到yield函數至少執行1次以後,才能調用send()方法,這是由於send()會在內部使生成器掛起,因此必需要求yield已經執行過,若是去掉上述代碼中的第一個r.next(),則會獲得錯誤:TypeError: can't send non-None value to a just-started generator。若是必定要在剛剛啓動的生成器使用send()方法,能夠採用send(None)來解決。
相關文章
相關標籤/搜索