每次去看別人寫的代碼都有個yield,感覺超級geek,今天花點兒時間整理下。
按照順序來:
1. iterator
iterator叫做迭代器,用來遍歷可以序列化的數據,比如一個list,set 等,當然如果對象想要能夠使用迭代器來遍歷,只要在該對象的類中添加__iter__()方法,該方法返回一個迭代器對象,迭代器對象中需要實現next()方法,例子如下:
>>> class sequenceClass(object):
... def __init__(self, *args):
... self._data = list(args)
... def __iter__(self):
... return DataIter(self)
...
>>> class DataIter(object):
... def __init__(self, data):
... self._index = 0
... self._data = data._data
... def next(self):
... if self._index >= len(self._data): raise StopIteration()
... d = self._data[self._index]
... self._index += 1
... return d
...
>>> data = sequenceClass(1,2,3,4)
>>> for x in data: print x
...
1
2
3
4
執行方法如下:
創建對象data : data = sequenceClass(1,2,3,4)
使用for循環遍歷data:
1.第一次for循環遍歷data時,調用了data的__iter__()方法,該方法返回一個迭代器對象:DataIter
2.接着for循環每次調用DataIter對象的next()方法,next()方法返回數據
3.等到數據遍歷完成時,拋出異常StopIteration, 該異常被for循環撲獲,遍歷結束。
以上是iterator的實現方法,但有一種更加簡單的方式,但初學時可能不太好理解,就是使用yield表達式來遍歷對象。
2.yield表達式及genetator
Python中,凡是含有yield的函數,被調用時都返回一個generator對象。generator設計的目的是能夠更加簡單的實現迭代器協議,例如上面的例子,需要單獨寫一個類來遍歷數據,而用generator,直接一個yield表達式就可以用做迭代器了。
看一個例子:
>>> def test_generator():
... s = 0
... for i in xrange(10):
... s = yield i * s
... print 'i:', i
... print 's:', s
... print 'i * s', i * s
...
>>> t = test_generator()
>>> t
<generator object test_generator at 0x10403a820>
>>>
>>> t.next()
0
>>> t.send(10)
i: 0
s: 10
i * s 0
10
>>> t.send(11)
i: 1
s: 11
i * s 11
22
一步步講解:
1.定義了一個函數叫做test_generator
2.調用test_generator,得到t對象
3.運行t時,是一個generator對象
4.t.next()或者t.send(None)啓動generator對象, generator對象運行到yield,將yield右面的表達式的值返回,並保存generator對象當前的狀,因此,後面的打印都沒有被執行。
5.t.send(10)
1)將輸入的值10賦值給s
2)執行yield後面的表達式,即將所有print執行完畢,解析如下:
由於在第4步generator保存了對象的當前的狀態,因此: i = 0, s是剛輸入的值爲10, i *s 當然就爲0了,接着進入下一次循環,此時i= 1,執行yield表達式,結果得10,保存當前generator狀態,返回10,generator停止運行。
6.t.send(11)
1)和第5步類似了,將11輸入給s
2)因爲第5步,generator保存了當前的對象的值,因此: i = 1, s是剛輸入的值爲11, i *s 當然就爲11了,接着進入下一次循環,此時i= 2,執行yield表達式,結果得22,保存當前generator狀態,返回22,generator停止運行。
7.接下來在調用t.send(num),和第5,6步一樣,直到generator內部的循環完成,當再次調用時,將拋出異常:StopIteration
總結一下:
1. yield表達式, 四種形式:
a. 不接受輸入值或者輸入值是None
yield 1
b. 接受輸入值
s = yield 1
c. 接受輸入,但不返回數據,這樣默認返回None
s = yield
d.既不接受輸入,也不返回值,默認返回None
yield
第一種:當函數調用到yield時,返回yield的右邊經過計算的值 ,這裏說計算的意思是,yield後面可以寫成函數,表達式等,
第二種:當函數調用到yield時,必須傳入一個值,該值存入s中,然後返回yield後面的表達式的值並保存當前狀態
第三種:只是將數據接受進來,然後執行yield後的語句,再次執行到yield時,保存當前狀態並返回,這樣的用例一般是
只打印一些處理消息,而不需要結果的方式。
第四種:這樣的只能遍歷generator內部的數據了。
2.使用generator的好處:
1)可以很方便的實現迭代器,即yield不接受輸入值
2)使用generator可以減少內存的使用,因爲遍歷出來的值都是每次進行計算的,不用一次性全部計算完成,然後一個個
遍歷,最終釋放掉;另外可以在yield後面做一些操作,可以很隨意的控制返回值。
3.coroutine 協程
使用yield的函數,若用send來控制,就算coroutine了,若用for循環來控制,就算generator或者iterator,使用方法大多見於
decorator, 如tornado中的gen.coroutine。