生成器能夠說是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的運行過程(如下爲我根據測試程序結果推斷的):
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()方法容許外界向生成器內部傳遞數據,當調用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()方法須要注意的有兩點:
TypeError: can't send non-None value to a just-started generator
。若是必定要在剛剛啓動的生成器使用send()方法,能夠採用send(None)來解決。